Merge "Ensure controller is attached before applying state"
diff --git a/Android.bp b/Android.bp
index ee5db70..933d1af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,7 +110,7 @@
 
         // AIDL sources from external directories
         ":android.hardware.graphics.common-V3-java-source",
-        ":android.hardware.security.keymint-V1-java-source",
+        ":android.hardware.security.keymint-V2-java-source",
         ":android.hardware.security.secureclock-V1-java-source",
         ":android.hardware.tv.tuner-V1-java-source",
         ":android.security.apc-java-source",
@@ -191,7 +191,6 @@
             "sax/java",
             "telecomm/java",
 
-            "apex/media/aidl/stable",
             // TODO(b/147699819): remove this
             "telephony/java",
         ],
@@ -289,6 +288,7 @@
             // TODO: remove when moved to the below package
             "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
             "hardware/interfaces/graphics/common/aidl",
         ],
     },
@@ -476,21 +476,28 @@
 }
 
 // TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
-metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
-    "--hide-package com.android.server " +
-    "--hide-package android.audio.policy.configuration.V7_0 " +
-    "--error UnhiddenSystemApi " +
-    "--hide RequiresPermission " +
-    "--hide CallbackInterface " +
-    "--hide MissingPermission --hide BroadcastBehavior " +
-    "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
-    "--error NoSettingsProvider " +
-    "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
+metalava_framework_docs_args = "" +
     "--api-lint-ignore-prefix android.icu. " +
     "--api-lint-ignore-prefix java. " +
     "--api-lint-ignore-prefix junit. " +
-    "--api-lint-ignore-prefix org. "
+    "--api-lint-ignore-prefix org. " +
+    "--error NoSettingsProvider " +
+    "--error UnhiddenSystemApi " +
+    "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
+    "--hide BroadcastBehavior " +
+    "--hide CallbackInterface " +
+    "--hide DeprecationMismatch " +
+    "--hide HiddenSuperclass " +
+    "--hide HiddenTypeParameter " +
+    "--hide MissingPermission " +
+    "--hide-package android.audio.policy.configuration.V7_0 " +
+    "--hide-package com.android.server " +
+    "--hide RequiresPermission " +
+    "--hide SdkConstant " +
+    "--hide Todo " +
+    "--hide Typo " +
+    "--hide UnavailableSymbol " +
+    "--manifest $(location core/res/AndroidManifest.xml) "
 
 packages_to_document = [
     "android",
@@ -538,6 +545,7 @@
             "frameworks/native/libs/permission/aidl",
             // TODO: remove when moved to the below package
             "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
             "packages/modules/Connectivity/framework/aidl-export",
             "hardware/interfaces/graphics/common/aidl",
         ],
@@ -575,17 +583,15 @@
 stubs_defaults {
     name: "module-classpath-stubs-defaults",
     aidl: {
-        local_include_dirs: [
-            "apex/media/aidl/stable",
-        ],
         include_dirs: [
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
         ],
     },
     libs: [
         "art.module.public.api",
         "sdk_module-lib_current_framework-tethering",
-        "sdk_module-lib_current_framework-connectivity-tiramisu",
+        "sdk_module-lib_current_framework-connectivity-t",
         "sdk_public_current_framework-bluetooth",
         // There are a few classes from modules used by the core that
         // need to be resolved by metalava. We use a prebuilt stub of the
diff --git a/Android.mk b/Android.mk
index 46529eb..d9e202c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,6 +15,14 @@
 #
 LOCAL_PATH := $(call my-dir)
 
+$(eval $(call declare-1p-copy-files,frameworks/base,.ogg))
+$(eval $(call declare-1p-copy-files,frameworks/base,.kl))
+$(eval $(call declare-1p-copy-files,frameworks/base,.kcm))
+$(eval $(call declare-1p-copy-files,frameworks/base,.idc))
+$(eval $(call declare-1p-copy-files,frameworks/base,dirty-image-objects))
+$(eval $(call declare-1p-copy-files,frameworks/base/config,))
+$(eval $(call declare-1p-copy-files,frameworks/native/data,))
+
 # Load framework-specific path mappings used later in the build.
 include $(LOCAL_PATH)/pathmap.mk
 
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 996cdc9..ba31161 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -145,11 +145,9 @@
         "api-versions-jars-dir",
     ],
     aidl: {
-        local_include_dirs: [
-            "apex/media/aidl/stable",
-        ],
         include_dirs: [
             "packages/modules/Connectivity/framework/aidl-export",
+            "packages/modules/Media/apex/aidl/stable",
         ],
     },
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 92e7dc9..94f4374 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -258,7 +258,7 @@
     srcs: [":module-lib-api-stubs-docs-non-updatable"],
     libs: [
         "sdk_module-lib_current_framework-tethering",
-        "sdk_module-lib_current_framework-connectivity-tiramisu",
+        "sdk_module-lib_current_framework-connectivity-t",
         "sdk_public_current_framework-bluetooth",
         // NOTE: The below can be removed once the prebuilt stub contains bluetooth.
         "sdk_system_current_android",
@@ -374,6 +374,64 @@
     },
 }
 
+////////////////////////////////////////////////////////////////////////
+// api-versions.xml generation, for public and system. This API database
+// also contains the android.test.* APIs.
+////////////////////////////////////////////////////////////////////////
+
+java_library {
+    name: "android_stubs_current_with_test_libs",
+    static_libs: [
+        "android_stubs_current",
+        "android.test.base.stubs",
+        "android.test.mock.stubs",
+        "android.test.runner.stubs",
+    ],
+    defaults: ["android.jar_defaults"],
+    visibility: [
+        "//visibility:override",
+        "//visibility:private",
+    ],
+}
+
+java_library {
+    name: "android_system_stubs_current_with_test_libs",
+    static_libs: [
+        "android_system_stubs_current",
+        "android.test.base.stubs.system",
+        "android.test.mock.stubs.system",
+        "android.test.runner.stubs.system",
+    ],
+    defaults: ["android.jar_defaults"],
+    visibility: [
+        "//visibility:override",
+        "//visibility:private",
+    ],
+}
+
+droidstubs {
+    name: "api_versions_public",
+    srcs: [":android_stubs_current_with_test_libs{.jar}"],
+    generate_stubs: false,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+}
+
+droidstubs {
+    name: "api_versions_system",
+    srcs: [":android_system_stubs_current_with_test_libs{.jar}"],
+    generate_stubs: false,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+    api_levels_sdk_type: "system",
+}
+
 /////////////////////////////////////////////////////////////////////
 // hwbinder.stubs provides APIs required for building HIDL Java
 // libraries.
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 8c8d2bf..88082f7 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -39,7 +39,9 @@
     /** @hide */
     public static final String KEY_AM_MAX_SATIATED_BALANCE = "am_max_satiated_balance";
     /** @hide */
-    public static final String KEY_AM_MAX_CIRCULATION = "am_max_circulation";
+    public static final String KEY_AM_INITIAL_CONSUMPTION_LIMIT = "am_initial_consumption_limit";
+    /** @hide */
+    public static final String KEY_AM_HARD_CONSUMPTION_LIMIT = "am_hard_consumption_limit";
     // TODO: Add AlarmManager modifier keys
     /** @hide */
     public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT =
@@ -163,7 +165,9 @@
     public static final String KEY_JS_MAX_SATIATED_BALANCE =
             "js_max_satiated_balance";
     /** @hide */
-    public static final String KEY_JS_MAX_CIRCULATION = "js_max_circulation";
+    public static final String KEY_JS_INITIAL_CONSUMPTION_LIMIT = "js_initial_consumption_limit";
+    /** @hide */
+    public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
     // TODO: Add JobScheduler modifier keys
     /** @hide */
     public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
@@ -280,7 +284,9 @@
     /** @hide */
     public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
     /** @hide */
-    public static final int DEFAULT_AM_MAX_CIRCULATION = 52000;
+    public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 28800;
+    /** @hide */
+    public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 52000;
     // TODO: add AlarmManager modifier default values
     /** @hide */
     public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
@@ -359,7 +365,7 @@
     // Default values JobScheduler factors
     // TODO: add time_since_usage variable to min satiated balance factors
     /** @hide */
-    public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 50000;
+    public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 20000;
     /** @hide */
     public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 10000;
     /** @hide */
@@ -367,7 +373,9 @@
     /** @hide */
     public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
     /** @hide */
-    public static final int DEFAULT_JS_MAX_CIRCULATION = 691200;
+    public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 460_000;
+    /** @hide */
+    public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 900_000;
     // TODO: add JobScheduler modifier default values
     /** @hide */
     public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 8237383..e9ce87f 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -3331,7 +3331,7 @@
         mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
         mMaintenanceStartTime = 0;
         mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
-        cancelLightAlarmLocked();
+        cancelAllLightAlarmsLocked();
     }
 
     @GuardedBy("this")
@@ -3406,7 +3406,7 @@
                     mLightState == LIGHT_STATE_IDLE
                             || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK;
         } else {
-            Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state");
+            Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state: " + mLightState);
             return;
         }
 
@@ -3441,7 +3441,7 @@
                 // connectivity...  let's try to wait until the network comes back.
                 // We'll only wait for another full idle period, however, and then give up.
                 scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
-                mNextLightAlarmTime = 0;
+                cancelLightAlarmLocked();
                 if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
                 mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
                 EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3467,7 +3467,7 @@
             // We're entering IDLE. We may have used less than curLightIdleBudget for the
             // maintenance window, so reschedule the alarm starting from now.
             scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
-            mNextLightAlarmTime = 0;
+            cancelLightAlarmLocked();
             if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
             mLightState = LIGHT_STATE_IDLE;
             EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3609,7 +3609,7 @@
                 moveToStateLocked(STATE_IDLE, reason);
                 if (mLightState != LIGHT_STATE_OVERRIDE) {
                     mLightState = LIGHT_STATE_OVERRIDE;
-                    cancelLightAlarmLocked();
+                    cancelAllLightAlarmsLocked();
                 }
                 addEvent(EVENT_DEEP_IDLE, null);
                 mGoingIdleWakeLock.acquire();
@@ -3950,11 +3950,21 @@
     }
 
     @GuardedBy("this")
-    void cancelLightAlarmLocked() {
+    private void cancelAllLightAlarmsLocked() {
+        cancelLightAlarmLocked();
+        cancelLightMaintenanceAlarmLocked();
+    }
+
+    @GuardedBy("this")
+    private void cancelLightAlarmLocked() {
         if (mNextLightAlarmTime != 0) {
             mNextLightAlarmTime = 0;
             mAlarmManager.cancel(mLightAlarmListener);
         }
+    }
+
+    @GuardedBy("this")
+    private void cancelLightMaintenanceAlarmLocked() {
         if (mNextLightMaintenanceAlarmTime != 0) {
             mNextLightMaintenanceAlarmTime = 0;
             mAlarmManager.cancel(mLightMaintenanceAlarmListener);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index a5c2bcc..90ec700 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -149,6 +149,8 @@
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import libcore.util.EmptyArray;
 
 import java.io.FileDescriptor;
@@ -2869,6 +2871,7 @@
                 packageName, UserHandle.of(userId));
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     void dumpImpl(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.println("Current Alarm Manager state:");
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 bdfdd55..b936278 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -127,6 +127,8 @@
 import com.android.server.utils.quota.Category;
 import com.android.server.utils.quota.CountQuotaTracker;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import libcore.util.EmptyArray;
 
 import java.io.FileDescriptor;
@@ -3005,7 +3007,7 @@
                     }
                 } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis());
+                        Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis());
                     }
                     if (mCharging) {
                         mCharging = false;
@@ -3821,6 +3823,7 @@
         });
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
         final int filterAppId = UserHandle.getAppId(filterUid);
         final long now = sSystemClock.millis();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 649aa39..efcf14f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -56,6 +56,8 @@
 import com.android.server.job.JobStatusDumpProto;
 import com.android.server.job.JobStatusShortInfoProto;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1971,6 +1973,7 @@
     }
 
     // Dumpsys infrastructure
+    @NeverCompile // Avoid size overhead of debugging code.
     public void dump(IndentingPrintWriter pw,  boolean full, long nowElapsed) {
         UserHandle.formatUid(pw, callingUid);
         pw.print(" tag="); pw.println(tag);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index b96055f..2b0a833 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -76,6 +76,8 @@
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 import com.android.server.utils.AlarmQueue;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
@@ -2169,7 +2171,7 @@
                     }
                     scheduleCutoff();
                 } else if (mRegularJobTimer && priorityLowered) {
-                    scheduleCutoff();
+                    rescheduleCutoff();
                 }
             }
         }
@@ -2205,7 +2207,7 @@
                                 mRunningBgJobs.valueAt(i).getEffectivePriority());
                     }
                     if (mLowestPriority != oldPriority) {
-                        scheduleCutoff();
+                        rescheduleCutoff();
                     }
                 }
             }
@@ -2705,7 +2707,7 @@
                             if (DEBUG) {
                                 Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
                             }
-                            mPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+                            mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
                         }
                         break;
                     }
@@ -2727,7 +2729,7 @@
                             if (DEBUG) {
                                 Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
                             }
-                            mEJPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+                            mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
                         }
                         break;
                     }
@@ -4358,6 +4360,7 @@
 
     //////////////////////////// DATA DUMP //////////////////////////////
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index a6a007f..c0a8148 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -55,9 +55,6 @@
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.utils.AlarmQueue;
 
-import libcore.util.EmptyArray;
-
-import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
@@ -105,54 +102,13 @@
     private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue;
 
     /**
-     * Comparator to use to sort apps before we distribute ARCs so that we try to give the most
-     * important apps ARCs first.
+     * Check the affordability notes of all apps.
      */
-    @VisibleForTesting
-    final Comparator<PackageInfo> mPackageDistributionComparator =
-            new Comparator<PackageInfo>() {
-                @Override
-                public int compare(PackageInfo pi1, PackageInfo pi2) {
-                    final ApplicationInfo appInfo1 = pi1.applicationInfo;
-                    final ApplicationInfo appInfo2 = pi2.applicationInfo;
-                    // Put any packages that don't declare an application at the end. A missing
-                    // <application> tag likely means the app won't be doing any work anyway.
-                    if (appInfo1 == null) {
-                        if (appInfo2 == null) {
-                            return 0;
-                        }
-                        return 1;
-                    } else if (appInfo2 == null) {
-                        return -1;
-                    }
-                    // Privileged apps eat first. They're likely required for the device to
-                    // function properly.
-                    // TODO: include headless system apps
-                    if (appInfo1.isPrivilegedApp()) {
-                        if (!appInfo2.isPrivilegedApp()) {
-                            return -1;
-                        }
-                    } else if (appInfo2.isPrivilegedApp()) {
-                        return 1;
-                    }
-
-                    // Sort by most recently used.
-                    final long timeSinceLastUsedMs1 =
-                            mAppStandbyInternal.getTimeSinceLastUsedByUser(
-                                    pi1.packageName, UserHandle.getUserId(pi1.applicationInfo.uid));
-                    final long timeSinceLastUsedMs2 =
-                            mAppStandbyInternal.getTimeSinceLastUsedByUser(
-                                    pi2.packageName, UserHandle.getUserId(pi2.applicationInfo.uid));
-                    if (timeSinceLastUsedMs1 < timeSinceLastUsedMs2) {
-                        return -1;
-                    } else if (timeSinceLastUsedMs1 > timeSinceLastUsedMs2) {
-                        return 1;
-                    }
-                    return 0;
-                }
-            };
-
-    private static final int MSG_CHECK_BALANCE = 0;
+    private static final int MSG_CHECK_ALL_AFFORDABILITY = 0;
+    /**
+     * Check the affordability notes of a single app.
+     */
+    private static final int MSG_CHECK_INDIVIDUAL_AFFORDABILITY = 1;
 
     Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
         mLock = irs.getLock();
@@ -179,7 +135,7 @@
 
         @Override
         public void accept(OngoingEvent ongoingEvent) {
-            mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+            mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow).price;
         }
     }
 
@@ -204,6 +160,11 @@
     }
 
     @GuardedBy("mLock")
+    private boolean isAffordableLocked(long balance, long price, long ctp) {
+        return balance >= price && mScribe.getRemainingConsumableNarcsLocked() >= ctp;
+    }
+
+    @GuardedBy("mLock")
     void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
             final int eventId, @Nullable String tag) {
         if (mIrs.isSystem(userId, pkgName)) {
@@ -218,10 +179,13 @@
         final int eventType = getEventType(eventId);
         switch (eventType) {
             case TYPE_ACTION:
-                final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+                final EconomicPolicy.Cost actionCost =
+                        economicPolicy.getCostOfAction(eventId, userId, pkgName);
 
                 recordTransactionLocked(userId, pkgName, ledger,
-                        new Ledger.Transaction(now, now, eventId, tag, -actionCost), true);
+                        new Ledger.Transaction(now, now, eventId, tag,
+                                -actionCost.price, actionCost.costToProduce),
+                        true);
                 break;
 
             case TYPE_REWARD:
@@ -231,7 +195,7 @@
                     final long rewardVal = Math.max(0,
                             Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
                     recordTransactionLocked(userId, pkgName, ledger,
-                            new Ledger.Transaction(now, now, eventId, tag, rewardVal), true);
+                            new Ledger.Transaction(now, now, eventId, tag, rewardVal, 0), true);
                 }
                 break;
 
@@ -268,11 +232,12 @@
         final int eventType = getEventType(eventId);
         switch (eventType) {
             case TYPE_ACTION:
-                final long actionCost = economicPolicy.getCostOfAction(eventId, userId, pkgName);
+                final EconomicPolicy.Cost actionCost =
+                        economicPolicy.getCostOfAction(eventId, userId, pkgName);
 
                 if (ongoingEvent == null) {
                     ongoingEvents.add(eventId, tag,
-                            new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+                            new OngoingEvent(eventId, tag, startElapsed, actionCost));
                 } else {
                     ongoingEvent.refCount++;
                 }
@@ -283,7 +248,7 @@
                 if (reward != null) {
                     if (ongoingEvent == null) {
                         ongoingEvents.add(eventId, tag, new OngoingEvent(
-                                eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+                                eventId, tag, startElapsed, reward));
                     } else {
                         ongoingEvent.refCount++;
                     }
@@ -306,52 +271,7 @@
 
     @GuardedBy("mLock")
     void onPricingChangedLocked() {
-        final long now = getCurrentTimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
-        final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
-
-        mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
-            final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
-                    mActionAffordabilityNotes.get(userId, pkgName);
-            final boolean[] wasAffordable;
-            if (actionAffordabilityNotes != null) {
-                final int size = actionAffordabilityNotes.size();
-                wasAffordable = new boolean[size];
-                for (int i = 0; i < size; ++i) {
-                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
-                    final long originalBalance =
-                            mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
-                    wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
-                }
-            } else {
-                wasAffordable = EmptyArray.BOOLEAN;
-            }
-            ongoingEvents.forEach((ongoingEvent) -> {
-                // Disable balance check & affordability notifications here because we're in the
-                // middle of updating ongoing action costs/prices and sending out notifications
-                // or rescheduling the balance check alarm would be a waste since we'll have to
-                // redo them again after all of our internal state is updated.
-                stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
-                        ongoingEvent.tag, nowElapsed, now, false, false);
-                noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
-                        nowElapsed, false);
-            });
-            if (actionAffordabilityNotes != null) {
-                final int size = actionAffordabilityNotes.size();
-                for (int i = 0; i < size; ++i) {
-                    final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
-                    note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
-                    final long newBalance =
-                            mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
-                    final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
-                    if (wasAffordable[i] != isAffordable) {
-                        note.setNewAffordability(isAffordable);
-                        mIrs.postAffordabilityChanged(userId, pkgName, note);
-                    }
-                }
-            }
-            scheduleBalanceCheckLocked(userId, pkgName);
-        });
+        onAnythingChangedLocked(true);
     }
 
     @GuardedBy("mLock")
@@ -365,40 +285,21 @@
             SparseArrayMap<String, OngoingEvent> ongoingEvents =
                     mCurrentOngoingEvents.get(userId, pkgName);
             if (ongoingEvents != null) {
+                mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+                ongoingEvents.forEach(mOngoingEventUpdater);
                 final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
                         mActionAffordabilityNotes.get(userId, pkgName);
-                final boolean[] wasAffordable;
                 if (actionAffordabilityNotes != null) {
                     final int size = actionAffordabilityNotes.size();
-                    wasAffordable = new boolean[size];
+                    final long newBalance =
+                            mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
                     for (int n = 0; n < size; ++n) {
                         final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
-                        final long originalBalance =
-                                mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
-                        wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
-                    }
-                } else {
-                    wasAffordable = EmptyArray.BOOLEAN;
-                }
-                ongoingEvents.forEach((ongoingEvent) -> {
-                    // Disable balance check & affordability notifications here because we're in the
-                    // middle of updating ongoing action costs/prices and sending out notifications
-                    // or rescheduling the balance check alarm would be a waste since we'll have to
-                    // redo them again after all of our internal state is updated.
-                    stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
-                            ongoingEvent.tag, nowElapsed, now, false, false);
-                    noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
-                            nowElapsed, false);
-                });
-                if (actionAffordabilityNotes != null) {
-                    final int size = actionAffordabilityNotes.size();
-                    for (int n = 0; n < size; ++n) {
-                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
-                        note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
-                        final long newBalance =
-                                mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
-                        final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
-                        if (wasAffordable[n] != isAffordable) {
+                        note.recalculateCosts(economicPolicy, userId, pkgName);
+                        final boolean isAffordable =
+                                isAffordableLocked(newBalance,
+                                        note.getCachedModifiedPrice(), note.getCtp());
+                        if (note.isCurrentlyAffordable() != isAffordable) {
                             note.setNewAffordability(isAffordable);
                             mIrs.postAffordabilityChanged(userId, pkgName, note);
                         }
@@ -410,15 +311,68 @@
     }
 
     @GuardedBy("mLock")
+    private void onAnythingChangedLocked(final boolean updateOngoingEvents) {
+        final long now = getCurrentTimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
+
+        for (int uIdx = mCurrentOngoingEvents.numMaps() - 1; uIdx >= 0; --uIdx) {
+            final int userId = mCurrentOngoingEvents.keyAt(uIdx);
+
+            for (int pIdx = mCurrentOngoingEvents.numElementsForKey(userId) - 1; pIdx >= 0;
+                    --pIdx) {
+                final String pkgName = mCurrentOngoingEvents.keyAt(uIdx, pIdx);
+
+                SparseArrayMap<String, OngoingEvent> ongoingEvents =
+                        mCurrentOngoingEvents.valueAt(uIdx, pIdx);
+                if (ongoingEvents != null) {
+                    if (updateOngoingEvents) {
+                        mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+                        ongoingEvents.forEach(mOngoingEventUpdater);
+                    }
+                    scheduleBalanceCheckLocked(userId, pkgName);
+                }
+            }
+        }
+        for (int uIdx = mActionAffordabilityNotes.numMaps() - 1; uIdx >= 0; --uIdx) {
+            final int userId = mActionAffordabilityNotes.keyAt(uIdx);
+
+            for (int pIdx = mActionAffordabilityNotes.numElementsForKey(userId) - 1; pIdx >= 0;
+                    --pIdx) {
+                final String pkgName = mActionAffordabilityNotes.keyAt(uIdx, pIdx);
+
+                final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+                        mActionAffordabilityNotes.valueAt(uIdx, pIdx);
+
+                if (actionAffordabilityNotes != null) {
+                    final int size = actionAffordabilityNotes.size();
+                    final long newBalance = getBalanceLocked(userId, pkgName);
+                    for (int n = 0; n < size; ++n) {
+                        final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+                        note.recalculateCosts(economicPolicy, userId, pkgName);
+                        final boolean isAffordable =
+                                isAffordableLocked(newBalance,
+                                        note.getCachedModifiedPrice(), note.getCtp());
+                        if (note.isCurrentlyAffordable() != isAffordable) {
+                            note.setNewAffordability(isAffordable);
+                            mIrs.postAffordabilityChanged(userId, pkgName, note);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
     void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
             @Nullable String tag, final long nowElapsed, final long now) {
         stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true, true);
     }
 
     /**
-     * @param updateBalanceCheck          Whether or not to reschedule the affordability/balance
+     * @param updateBalanceCheck          Whether to reschedule the affordability/balance
      *                                    check alarm.
-     * @param notifyOnAffordabilityChange Whether or not to evaluate the app's ability to afford
+     * @param notifyOnAffordabilityChange Whether to evaluate the app's ability to afford
      *                                    registered bills and notify listeners about any changes.
      */
     @GuardedBy("mLock")
@@ -453,9 +407,11 @@
         if (ongoingEvent.refCount <= 0) {
             final long startElapsed = ongoingEvent.startTimeElapsed;
             final long startTime = now - (nowElapsed - startElapsed);
-            final long actualDelta = getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+            final EconomicPolicy.Cost actualDelta =
+                    getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
             recordTransactionLocked(userId, pkgName, ledger,
-                    new Ledger.Transaction(startTime, now, eventId, tag, actualDelta),
+                    new Ledger.Transaction(startTime, now, eventId, tag, actualDelta.price,
+                            actualDelta.costToProduce),
                     notifyOnAffordabilityChange);
 
             ongoingEvents.delete(eventId, tag);
@@ -466,17 +422,20 @@
     }
 
     @GuardedBy("mLock")
-    private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
-            long nowElapsed, long now) {
+    @NonNull
+    private EconomicPolicy.Cost getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent,
+            @NonNull Ledger ledger, long nowElapsed, long now) {
         final long startElapsed = ongoingEvent.startTimeElapsed;
         final long durationSecs = (nowElapsed - startElapsed) / 1000;
-        final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+        final long computedDelta = durationSecs * ongoingEvent.getDeltaPerSec();
         if (ongoingEvent.reward == null) {
-            return computedDelta;
+            return new EconomicPolicy.Cost(
+                    durationSecs * ongoingEvent.getCtpPerSec(), computedDelta);
         }
         final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
-        return Math.max(0,
-                Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+        return new EconomicPolicy.Cost(0,
+                Math.max(0,
+                        Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta)));
     }
 
     @VisibleForTesting
@@ -494,22 +453,6 @@
             return;
         }
         final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
-        final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
-        final long curNarcsInCirculation = mScribe.getNarcsInCirculationLocked();
-        final long newArcsInCirculation = curNarcsInCirculation + transaction.delta;
-        if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
-            // Set lower bound at 0 so we don't accidentally take away credits when we were trying
-            // to _give_ the app credits.
-            final long newDelta = Math.max(0, maxCirculationAllowed - curNarcsInCirculation);
-            Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
-                    + eventToString(transaction.eventId)
-                    + (transaction.tag == null ? "" : ":" + transaction.tag)
-                    + " for " + appToString(userId, pkgName)
-                    + " by " + narcToString(transaction.delta - newDelta));
-            transaction = new Ledger.Transaction(
-                    transaction.startTimeMs, transaction.endTimeMs,
-                    transaction.eventId, transaction.tag, newDelta);
-        }
         final long originalBalance = ledger.getCurrentBalance();
         if (transaction.delta > 0
                 && originalBalance + transaction.delta > economicPolicy.getMaxSatiatedBalance()) {
@@ -524,10 +467,10 @@
                     + " by " + narcToString(transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
-                    transaction.eventId, transaction.tag, newDelta);
+                    transaction.eventId, transaction.tag, newDelta, transaction.ctp);
         }
         ledger.recordTransaction(transaction);
-        mScribe.adjustNarcsInCirculationLocked(transaction.delta);
+        mScribe.adjustRemainingConsumableNarcsLocked(-transaction.ctp);
         if (transaction.delta != 0 && notifyOnAffordabilityChange) {
             final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
                     mActionAffordabilityNotes.get(userId, pkgName);
@@ -535,7 +478,9 @@
                 final long newBalance = ledger.getCurrentBalance();
                 for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
                     final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
-                    final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+                    final boolean isAffordable =
+                            isAffordableLocked(newBalance,
+                                    note.getCachedModifiedPrice(), note.getCtp());
                     if (note.isCurrentlyAffordable() != isAffordable) {
                         note.setNewAffordability(isAffordable);
                         mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -543,6 +488,10 @@
                 }
             }
         }
+        if (transaction.ctp != 0) {
+            mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+            mIrs.maybePerformQuantitativeEasingLocked();
+        }
     }
 
     /**
@@ -599,8 +548,8 @@
                         }
 
                         recordTransactionLocked(userId, pkgName, ledger,
-                                new Ledger.Transaction(
-                                        now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+                                new Ledger.Transaction(now, now, REGULATION_WEALTH_RECLAMATION,
+                                        null, -toReclaim, 0),
                                 true);
                     }
                 }
@@ -648,7 +597,7 @@
             final long now = getCurrentTimeMillis();
             final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
             recordTransactionLocked(userId, pkgName, ledger,
-                    new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim),
+                    new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim, 0),
                     true);
         }
     }
@@ -665,10 +614,13 @@
         return !mIrs.isSystem(userId, packageInfo.packageName);
     }
 
+    void onCreditSupplyChanged() {
+        mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY);
+    }
+
     @GuardedBy("mLock")
     void distributeBasicIncomeLocked(int batteryLevel) {
         List<PackageInfo> pkgs = mIrs.getInstalledPackages();
-        pkgs.sort(mPackageDistributionComparator);
 
         final long now = getCurrentTimeMillis();
         for (int i = 0; i < pkgs.size(); ++i) {
@@ -686,7 +638,7 @@
             if (shortfall > 0) {
                 recordTransactionLocked(userId, pkgName, ledger,
                         new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
-                                null, (long) (perc * shortfall)), true);
+                                null, (long) (perc * shortfall), 0), true);
             }
         }
     }
@@ -705,12 +657,8 @@
     @GuardedBy("mLock")
     void grantBirthrightsLocked(final int userId) {
         final List<PackageInfo> pkgs = mIrs.getInstalledPackages(userId);
-        final long maxBirthright =
-                mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
         final long now = getCurrentTimeMillis();
 
-        pkgs.sort(mPackageDistributionComparator);
-
         for (int i = 0; i < pkgs.size(); ++i) {
             final PackageInfo packageInfo = pkgs.get(i);
             if (!shouldGiveCredits(packageInfo)) {
@@ -726,7 +674,7 @@
 
             recordTransactionLocked(userId, pkgName, ledger,
                     new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
-                            Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))),
+                            mIrs.getMinBalanceLocked(userId, pkgName), 0),
                     true);
         }
     }
@@ -740,14 +688,11 @@
             return;
         }
 
-        List<PackageInfo> pkgs = mIrs.getInstalledPackages();
-        final int numPackages = pkgs.size();
-        final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
         final long now = getCurrentTimeMillis();
 
         recordTransactionLocked(userId, pkgName, ledger,
                 new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
-                        Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))), true);
+                        mIrs.getMinBalanceLocked(userId, pkgName), 0), true);
     }
 
     @GuardedBy("mLock")
@@ -762,7 +707,7 @@
         final long now = getCurrentTimeMillis();
 
         recordTransactionLocked(userId, pkgName, ledger,
-                new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing), true);
+                new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing, 0), true);
     }
 
     @GuardedBy("mLock")
@@ -779,7 +724,7 @@
     private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
         final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
         if (ledger.getCurrentBalance() != 0) {
-            mScribe.adjustNarcsInCirculationLocked(-ledger.getCurrentBalance());
+            mScribe.adjustRemainingConsumableNarcsLocked(-ledger.getCurrentBalance());
         }
         mScribe.discardLedgerLocked(userId, pkgName);
         mCurrentOngoingEvents.delete(userId, pkgName);
@@ -803,6 +748,7 @@
         static final long WILL_NOT_CROSS_THRESHOLD = -1;
 
         private long mCurBalance;
+        private long mRemainingConsumableCredits;
         /**
          * The maximum change in credits per second towards the upper threshold
          * {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never
@@ -815,15 +761,25 @@
          * result in the app crossing the lower threshold.
          */
         private long mMaxDeltaPerSecToLowerThreshold;
+        /**
+         * The maximum change in credits per second towards the highest CTP threshold below the
+         * remaining consumable credits (cached in {@link #mCtpThreshold}). A value of 0 means
+         * the current ongoing events will never result in the app crossing the lower threshold.
+         */
+        private long mMaxDeltaPerSecToCtpThreshold;
         private long mUpperThreshold;
         private long mLowerThreshold;
+        private long mCtpThreshold;
 
-        void reset(long curBalance,
+        void reset(long curBalance, long remainingConsumableCredits,
                 @Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) {
             mCurBalance = curBalance;
+            mRemainingConsumableCredits = remainingConsumableCredits;
             mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0;
+            mMaxDeltaPerSecToCtpThreshold = 0;
             mUpperThreshold = Long.MIN_VALUE;
             mLowerThreshold = Long.MAX_VALUE;
+            mCtpThreshold = 0;
             if (actionAffordabilityNotes != null) {
                 for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
                     final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
@@ -835,6 +791,10 @@
                         mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
                                 ? price : Math.min(mUpperThreshold, price);
                     }
+                    final long ctp = note.getCtp();
+                    if (ctp <= mRemainingConsumableCredits) {
+                        mCtpThreshold = Math.max(mCtpThreshold, ctp);
+                    }
                 }
             }
         }
@@ -847,13 +807,23 @@
          * threshold.
          */
         long getTimeToCrossLowerThresholdMs() {
-            if (mMaxDeltaPerSecToLowerThreshold == 0) {
-                // Will never cross upper threshold based on current events.
+            if (mMaxDeltaPerSecToLowerThreshold == 0 && mMaxDeltaPerSecToCtpThreshold == 0) {
+                // Will never cross lower threshold based on current events.
                 return WILL_NOT_CROSS_THRESHOLD;
             }
-            // deltaPerSec is a negative value, so do threshold-balance to cancel out the negative.
-            final long minSeconds =
-                    (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+            long minSeconds = Long.MAX_VALUE;
+            if (mMaxDeltaPerSecToLowerThreshold != 0) {
+                // deltaPerSec is a negative value, so do threshold-balance to cancel out the
+                // negative.
+                minSeconds = (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+            }
+            if (mMaxDeltaPerSecToCtpThreshold != 0) {
+                minSeconds = Math.min(minSeconds,
+                        // deltaPerSec is a negative value, so do threshold-balance to cancel
+                        // out the negative.
+                        (mCtpThreshold - mRemainingConsumableCredits)
+                                / mMaxDeltaPerSecToCtpThreshold);
+            }
             return minSeconds * 1000;
         }
 
@@ -876,10 +846,15 @@
 
         @Override
         public void accept(OngoingEvent ongoingEvent) {
-            if (mCurBalance >= mLowerThreshold && ongoingEvent.deltaPerSec < 0) {
-                mMaxDeltaPerSecToLowerThreshold += ongoingEvent.deltaPerSec;
-            } else if (mCurBalance < mUpperThreshold && ongoingEvent.deltaPerSec > 0) {
-                mMaxDeltaPerSecToUpperThreshold += ongoingEvent.deltaPerSec;
+            final long deltaPerSec = ongoingEvent.getDeltaPerSec();
+            if (mCurBalance >= mLowerThreshold && deltaPerSec < 0) {
+                mMaxDeltaPerSecToLowerThreshold += deltaPerSec;
+            } else if (mCurBalance < mUpperThreshold && deltaPerSec > 0) {
+                mMaxDeltaPerSecToUpperThreshold += deltaPerSec;
+            }
+            final long ctpPerSec = ongoingEvent.getCtpPerSec();
+            if (mRemainingConsumableCredits >= mCtpThreshold && deltaPerSec < 0) {
+                mMaxDeltaPerSecToCtpThreshold -= ctpPerSec;
             }
         }
     }
@@ -896,8 +871,9 @@
             mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
             return;
         }
-        mTrendCalculator.reset(
-                getBalanceLocked(userId, pkgName), mActionAffordabilityNotes.get(userId, pkgName));
+        mTrendCalculator.reset(getBalanceLocked(userId, pkgName),
+                mScribe.getRemainingConsumableNarcsLocked(),
+                mActionAffordabilityNotes.get(userId, pkgName));
         ongoingEvents.forEach(mTrendCalculator);
         final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
         final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs();
@@ -931,20 +907,79 @@
         public final String tag;
         @Nullable
         public final EconomicPolicy.Reward reward;
-        public final long deltaPerSec;
+        @Nullable
+        public final EconomicPolicy.Cost actionCost;
         public int refCount;
 
-        OngoingEvent(int eventId, @Nullable String tag,
-                @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+        OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+                @NonNull EconomicPolicy.Reward reward) {
             this.startTimeElapsed = startTimeElapsed;
             this.eventId = eventId;
             this.tag = tag;
             this.reward = reward;
-            this.deltaPerSec = deltaPerSec;
+            this.actionCost = null;
             refCount = 1;
         }
+
+        OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed,
+                @NonNull EconomicPolicy.Cost actionCost) {
+            this.startTimeElapsed = startTimeElapsed;
+            this.eventId = eventId;
+            this.tag = tag;
+            this.reward = null;
+            this.actionCost = actionCost;
+            refCount = 1;
+        }
+
+        long getDeltaPerSec() {
+            if (actionCost != null) {
+                return -actionCost.price;
+            }
+            if (reward != null) {
+                return reward.ongoingRewardPerSecond;
+            }
+            Slog.wtfStack(TAG, "No action or reward in ongoing event?!??!");
+            return 0;
+        }
+
+        long getCtpPerSec() {
+            if (actionCost != null) {
+                return actionCost.costToProduce;
+            }
+            return 0;
+        }
     }
 
+    private class OngoingEventUpdater implements Consumer<OngoingEvent> {
+        private int mUserId;
+        private String mPkgName;
+        private long mNow;
+        private long mNowElapsed;
+
+        private void reset(int userId, String pkgName, long now, long nowElapsed) {
+            mUserId = userId;
+            mPkgName = pkgName;
+            mNow = now;
+            mNowElapsed = nowElapsed;
+        }
+
+        @Override
+        public void accept(OngoingEvent ongoingEvent) {
+            // Disable balance check & affordability notifications here because
+            // we're in the middle of updating ongoing action costs/prices and
+            // sending out notifications or rescheduling the balance check alarm
+            // would be a waste since we'll have to redo them again after all of
+            // our internal state is updated.
+            final boolean updateBalanceCheck = false;
+            stopOngoingActionLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+                    mNowElapsed, mNow, updateBalanceCheck, /* notifyOnAffordabilityChange */ false);
+            noteOngoingEventLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag,
+                    mNowElapsed, updateBalanceCheck);
+        }
+    }
+
+    private final OngoingEventUpdater mOngoingEventUpdater = new OngoingEventUpdater();
+
     private static final class Package {
         public final String packageName;
         public final int userId;
@@ -996,7 +1031,8 @@
         protected void processExpiredAlarms(@NonNull ArraySet<Package> expired) {
             for (int i = 0; i < expired.size(); ++i) {
                 Package p = expired.valueAt(i);
-                mHandler.obtainMessage(MSG_CHECK_BALANCE, p.userId, 0, p.packageName)
+                mHandler.obtainMessage(
+                        MSG_CHECK_INDIVIDUAL_AFFORDABILITY, p.userId, 0, p.packageName)
                         .sendToTarget();
             }
         }
@@ -1023,9 +1059,10 @@
                 note.setNewAffordability(true);
                 return;
             }
-            note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
+            note.recalculateCosts(economicPolicy, userId, pkgName);
             note.setNewAffordability(
-                    getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice());
+                    isAffordableLocked(getBalanceLocked(userId, pkgName),
+                            note.getCachedModifiedPrice(), note.getCtp()));
             mIrs.postAffordabilityChanged(userId, pkgName, note);
             // Update ongoing alarm
             scheduleBalanceCheckLocked(userId, pkgName);
@@ -1052,6 +1089,7 @@
     static final class ActionAffordabilityNote {
         private final EconomyManagerInternal.ActionBill mActionBill;
         private final EconomyManagerInternal.AffordabilityChangeListener mListener;
+        private long mCtp;
         private long mModifiedPrice;
         private boolean mIsAffordable;
 
@@ -1086,22 +1124,29 @@
             return mModifiedPrice;
         }
 
+        private long getCtp() {
+            return mCtp;
+        }
+
         @VisibleForTesting
-        long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
+        void recalculateCosts(@NonNull EconomicPolicy economicPolicy,
                 int userId, @NonNull String pkgName) {
             long modifiedPrice = 0;
+            long ctp = 0;
             final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
                     mActionBill.getAnticipatedActions();
             for (int i = 0; i < anticipatedActions.size(); ++i) {
                 final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
 
-                final long actionCost =
+                final EconomicPolicy.Cost actionCost =
                         economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
-                modifiedPrice += actionCost * aa.numInstantaneousCalls
-                        + actionCost * (aa.ongoingDurationMs / 1000);
+                modifiedPrice += actionCost.price * aa.numInstantaneousCalls
+                        + actionCost.price * (aa.ongoingDurationMs / 1000);
+                ctp += actionCost.costToProduce * aa.numInstantaneousCalls
+                        + actionCost.costToProduce * (aa.ongoingDurationMs / 1000);
             }
             mModifiedPrice = modifiedPrice;
-            return modifiedPrice;
+            mCtp = ctp;
         }
 
         boolean isCurrentlyAffordable() {
@@ -1138,7 +1183,15 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_CHECK_BALANCE: {
+                case MSG_CHECK_ALL_AFFORDABILITY: {
+                    synchronized (mLock) {
+                        removeMessages(MSG_CHECK_ALL_AFFORDABILITY);
+                        onAnythingChangedLocked(false);
+                    }
+                }
+                break;
+
+                case MSG_CHECK_INDIVIDUAL_AFFORDABILITY: {
                     final int userId = msg.arg1;
                     final String pkgName = (String) msg.obj;
                     synchronized (mLock) {
@@ -1151,8 +1204,8 @@
                             for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
                                 final ActionAffordabilityNote note =
                                         actionAffordabilityNotes.valueAt(i);
-                                final boolean isAffordable =
-                                        newBalance >= note.getCachedModifiedPrice();
+                                final boolean isAffordable = isAffordableLocked(
+                                        newBalance, note.getCachedModifiedPrice(), note.getCtp());
                                 if (note.isCurrentlyAffordable() != isAffordable) {
                                     note.setNewAffordability(isAffordable);
                                     mIrs.postAffordabilityChanged(userId, pkgName, note);
@@ -1207,7 +1260,12 @@
                         pw.print(" runtime=");
                         TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
                         pw.print(" delta/sec=");
-                        pw.print(ongoingEvent.deltaPerSec);
+                        pw.print(narcToString(ongoingEvent.getDeltaPerSec()));
+                        final long ctp = ongoingEvent.getCtpPerSec();
+                        if (ctp != 0) {
+                            pw.print(" ctp/sec=");
+                            pw.print(narcToString(ongoingEvent.getCtpPerSec()));
+                        }
                         pw.print(" refCount=");
                         pw.print(ongoingEvent.refCount);
                         pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index e1e6e47..71e00cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -33,7 +33,8 @@
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
 import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -70,7 +71,8 @@
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
 import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
-import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
 import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -143,7 +145,8 @@
     private long mMinSatiatedBalanceExempted;
     private long mMinSatiatedBalanceOther;
     private long mMaxSatiatedBalance;
-    private long mMaxSatiatedCirculation;
+    private long mInitialSatiatedConsumptionLimit;
+    private long mHardSatiatedConsumptionLimit;
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final InternalResourceService mInternalResourceService;
@@ -179,8 +182,13 @@
     }
 
     @Override
-    long getMaxSatiatedCirculation() {
-        return mMaxSatiatedCirculation;
+    long getInitialSatiatedConsumptionLimit() {
+        return mInitialSatiatedConsumptionLimit;
+    }
+
+    @Override
+    long getHardSatiatedConsumptionLimit() {
+        return mHardSatiatedConsumptionLimit;
     }
 
     @NonNull
@@ -217,8 +225,11 @@
                 DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
         mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
                 DEFAULT_AM_MAX_SATIATED_BALANCE));
-        mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_AM_MAX_CIRCULATION,
-                DEFAULT_AM_MAX_CIRCULATION));
+        mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+                KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT));
+        mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+                arcToNarc(mParser.getInt(
+                        KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT)));
 
         final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
                 mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
@@ -357,7 +368,11 @@
         pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
         pw.decreaseIndent();
         pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
-        pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+        pw.print("Consumption limits: [");
+        pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+        pw.print(", ");
+        pw.print(narcToString(mHardSatiatedConsumptionLimit));
+        pw.println("]");
 
         pw.println();
         pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index a4e7b80..2109a85 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -34,7 +34,7 @@
     private final SparseArray<Reward> mRewards = new SparseArray<>();
     private final int[] mCostModifiers;
     private long mMaxSatiatedBalance;
-    private long mMaxSatiatedCirculation;
+    private long mConsumptionLimit;
 
     CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
         super(irs);
@@ -74,9 +74,9 @@
 
         max = 0;
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
-            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+            max += mEnabledEconomicPolicies.valueAt(i).getInitialSatiatedConsumptionLimit();
         }
-        mMaxSatiatedCirculation = max;
+        mConsumptionLimit = max;
     }
 
     @Override
@@ -94,8 +94,13 @@
     }
 
     @Override
-     long getMaxSatiatedCirculation() {
-        return mMaxSatiatedCirculation;
+    long getInitialSatiatedConsumptionLimit() {
+        return mConsumptionLimit;
+    }
+
+    @Override
+    long getHardSatiatedConsumptionLimit() {
+        return mConsumptionLimit;
     }
 
     @NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index c1177b2..1e48015 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -151,6 +151,16 @@
         }
     }
 
+    static class Cost {
+        public final long costToProduce;
+        public final long price;
+
+        Cost(long costToProduce, long price) {
+            this.costToProduce = costToProduce;
+            this.price = price;
+        }
+    }
+
     private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
 
     EconomicPolicy(@NonNull InternalResourceService irs) {
@@ -193,10 +203,18 @@
     abstract long getMaxSatiatedBalance();
 
     /**
-     * Returns the maximum number of narcs that should be in circulation at once when the device is
-     * at 100% battery.
+     * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+     * cycle. This is the initial limit. The system may choose to increase the limit over time,
+     * but the increased limit should never exceed the value returned from
+     * {@link #getHardSatiatedConsumptionLimit()}.
      */
-    abstract long getMaxSatiatedCirculation();
+    abstract long getInitialSatiatedConsumptionLimit();
+
+    /**
+     * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+     * cycle. This is the hard limit that should never be exceeded.
+     */
+    abstract long getHardSatiatedConsumptionLimit();
 
     /** Return the set of modifiers that should apply to this policy's costs. */
     @NonNull
@@ -211,10 +229,11 @@
     void dump(IndentingPrintWriter pw) {
     }
 
-    final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+    @NonNull
+    final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
         final Action action = getAction(actionId);
         if (action == null) {
-            return 0;
+            return new Cost(0, 0);
         }
         long ctp = action.costToProduce;
         long price = action.basePrice;
@@ -235,7 +254,7 @@
                     (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
             price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
         }
-        return price;
+        return new Cost(ctp, price);
     }
 
     private static void initModifier(@Modifier.CostModifier final int modifierId,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 36895a5..c934807 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -69,6 +69,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomicPolicy.Cost;
 import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
 
 import java.io.FileDescriptor;
@@ -100,6 +101,11 @@
     private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
     /** The amount of time to delay reclamation by after boot. */
     private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
+    /**
+     * The battery level above which we may consider quantitative easing (increasing the consumption
+     * limit).
+     */
+    private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50;
     private static final int PACKAGE_QUERY_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                     | PackageManager.MATCH_APEX;
@@ -122,44 +128,6 @@
     @GuardedBy("mLock")
     private CompleteEconomicPolicy mCompleteEconomicPolicy;
 
-    private static final class ReclamationConfig {
-        /**
-         * ARC circulation threshold (% circulating vs scaled maximum) above which this config
-         * should come into play.
-         */
-        public final double circulationPercentageThreshold;
-        /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
-        public final double reclamationPercentage;
-        /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
-        public final long minUsedTimeMs;
-        /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
-        public final boolean scaleMinBalance;
-
-        ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage,
-                long minUsedTimeMs, boolean scaleMinBalance) {
-            this.circulationPercentageThreshold = circulationPercentageThreshold;
-            this.reclamationPercentage = reclamationPercentage;
-            this.minUsedTimeMs = minUsedTimeMs;
-            this.scaleMinBalance = scaleMinBalance;
-        }
-    }
-
-    /**
-     * Sorted list of reclamation configs used to determine how many credits to force reclaim when
-     * the circulation percentage is too high. The list should *always* be sorted in descending
-     * order of {@link ReclamationConfig#circulationPercentageThreshold}.
-     */
-    @GuardedBy("mLock")
-    private final List<ReclamationConfig> mReclamationConfigs = List.of(
-            new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true),
-            new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true),
-            new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true),
-            new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true),
-            new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false),
-            new ReclamationConfig(
-                    .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false)
-    );
-
     @NonNull
     @GuardedBy("mLock")
     private final List<PackageInfo> mPkgCache = new ArrayList<>();
@@ -266,8 +234,7 @@
     private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
     private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
     private static final int MSG_PROCESS_USAGE_EVENT = 2;
-    private static final int MSG_MAYBE_FORCE_RECLAIM = 3;
-    private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
+    private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3;
     private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
 
     /**
@@ -362,8 +329,8 @@
     }
 
     @GuardedBy("mLock")
-    long getMaxCirculationLocked() {
-        return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+    long getConsumptionLimitLocked() {
+        return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100;
     }
 
     @GuardedBy("mLock")
@@ -372,6 +339,11 @@
                 / 100;
     }
 
+    @GuardedBy("mLock")
+    long getInitialSatiatedConsumptionLimitLocked() {
+        return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+    }
+
     int getUid(final int userId, @NonNull final String pkgName) {
         synchronized (mPackageToUidCache) {
             Integer uid = mPackageToUidCache.get(userId, pkgName);
@@ -403,12 +375,15 @@
     void onBatteryLevelChanged() {
         synchronized (mLock) {
             final int newBatteryLevel = getCurrentBatteryLevel();
-            if (newBatteryLevel > mCurrentBatteryLevel) {
+            final boolean increased = newBatteryLevel > mCurrentBatteryLevel;
+            if (increased) {
                 mAgent.distributeBasicIncomeLocked(newBatteryLevel);
-            } else if (newBatteryLevel < mCurrentBatteryLevel) {
-                mHandler.obtainMessage(MSG_MAYBE_FORCE_RECLAIM).sendToTarget();
+            } else if (newBatteryLevel == mCurrentBatteryLevel) {
+                Slog.wtf(TAG, "Battery level stayed the same");
+                return;
             }
             mCurrentBatteryLevel = newBatteryLevel;
+            adjustCreditSupplyLocked(increased);
         }
     }
 
@@ -546,6 +521,31 @@
         }
     }
 
+    /**
+     * Try to increase the consumption limit if apps are reaching the current limit too quickly.
+     */
+    @GuardedBy("mLock")
+    void maybePerformQuantitativeEasingLocked() {
+        // We don't need to increase the limit if the device runs out of consumable credits
+        // when the battery is low.
+        final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+        if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
+                || remainingConsumableNarcs > 0) {
+            return;
+        }
+        final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
+        final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD)
+                * currentConsumptionLimit / 100;
+        final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
+                mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
+        if (newConsumptionLimit != currentConsumptionLimit) {
+            Slog.i(TAG, "Increasing consumption limit from " + narcToString(currentConsumptionLimit)
+                    + " to " + narcToString(newConsumptionLimit));
+            mScribe.setConsumptionLimitLocked(newConsumptionLimit);
+            adjustCreditSupplyLocked(/* allowIncrease */ true);
+        }
+    }
+
     void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
             @NonNull Agent.ActionAffordabilityNote affordabilityNote) {
         if (DEBUG) {
@@ -560,6 +560,23 @@
     }
 
     @GuardedBy("mLock")
+    private void adjustCreditSupplyLocked(boolean allowIncrease) {
+        final long newLimit = getConsumptionLimitLocked();
+        final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+        if (remainingConsumableNarcs == newLimit) {
+            return;
+        }
+        if (remainingConsumableNarcs > newLimit) {
+            mScribe.adjustRemainingConsumableNarcsLocked(newLimit - remainingConsumableNarcs);
+        } else if (allowIncrease) {
+            final double perc = mCurrentBatteryLevel / 100d;
+            final long shortfall = newLimit - remainingConsumableNarcs;
+            mScribe.adjustRemainingConsumableNarcsLocked((long) (perc * shortfall));
+        }
+        mAgent.onCreditSupplyChanged();
+    }
+
+    @GuardedBy("mLock")
     private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
         if (!mIsEnabled) {
             return;
@@ -650,48 +667,6 @@
         }
     }
 
-    /**
-     * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently
-     * in circulation.
-     */
-    @GuardedBy("mLock")
-    private void maybeForceReclaimLocked() {
-        final long maxCirculation = getMaxCirculationLocked();
-        if (maxCirculation == 0) {
-            Slog.wtf(TAG, "Max scaled circulation is 0...");
-            mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true);
-            mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
-            scheduleUnusedWealthReclamationLocked();
-            return;
-        }
-        final long curCirculation = mScribe.getNarcsInCirculationLocked();
-        final double circulationPerc = 1.0 * curCirculation / maxCirculation;
-        if (DEBUG) {
-            Slog.d(TAG, "Circulation %: " + circulationPerc);
-        }
-        final int numConfigs = mReclamationConfigs.size();
-        if (numConfigs == 0) {
-            return;
-        }
-        // The configs are sorted in descending order of circulationPercentageThreshold, so we can
-        // short-circuit if the current circulation is lower than the lowest threshold.
-        if (circulationPerc
-                < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) {
-            return;
-        }
-        // TODO: maybe exclude apps we think will be launched in the next few hours
-        for (int i = 0; i < numConfigs; ++i) {
-            final ReclamationConfig config = mReclamationConfigs.get(i);
-            if (circulationPerc >= config.circulationPercentageThreshold) {
-                mAgent.reclaimUnusedAssetsLocked(
-                        config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance);
-                mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
-                scheduleUnusedWealthReclamationLocked();
-                break;
-            }
-        }
-    }
-
     private void registerListeners() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
@@ -731,8 +706,18 @@
             final boolean isFirstSetup = !mScribe.recordExists();
             if (isFirstSetup) {
                 mAgent.grantBirthrightsLocked();
+                mScribe.setConsumptionLimitLocked(
+                        mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
             } else {
                 mScribe.loadFromDiskLocked();
+                if (mScribe.getSatiatedConsumptionLimitLocked()
+                        < mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+                        || mScribe.getSatiatedConsumptionLimitLocked()
+                        > mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+                    // Reset the consumption limit since several factors may have changed.
+                    mScribe.setConsumptionLimitLocked(
+                            mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+                }
             }
             scheduleUnusedWealthReclamationLocked();
         }
@@ -787,14 +772,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_MAYBE_FORCE_RECLAIM: {
-                    removeMessages(MSG_MAYBE_FORCE_RECLAIM);
-                    synchronized (mLock) {
-                        maybeForceReclaimLocked();
-                    }
-                }
-                break;
-
                 case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
                     final SomeArgs args = (SomeArgs) msg.obj;
                     final int userId = args.argi1;
@@ -936,12 +913,13 @@
             synchronized (mLock) {
                 for (int i = 0; i < projectedActions.size(); ++i) {
                     AnticipatedAction action = projectedActions.get(i);
-                    final long cost = mCompleteEconomicPolicy.getCostOfAction(
+                    final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
                             action.actionId, userId, pkgName);
-                    requiredBalance += cost * action.numInstantaneousCalls
-                            + cost * (action.ongoingDurationMs / 1000);
+                    requiredBalance += cost.price * action.numInstantaneousCalls
+                            + cost.price * (action.ongoingDurationMs / 1000);
                 }
-                return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance;
+                return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
+                        && mScribe.getRemainingConsumableNarcsLocked() >= requiredBalance;
             }
         }
 
@@ -960,14 +938,17 @@
             synchronized (mLock) {
                 for (int i = 0; i < projectedActions.size(); ++i) {
                     AnticipatedAction action = projectedActions.get(i);
-                    final long cost = mCompleteEconomicPolicy.getCostOfAction(
+                    final Cost cost = mCompleteEconomicPolicy.getCostOfAction(
                             action.actionId, userId, pkgName);
-                    totalCostPerSecond += cost;
+                    totalCostPerSecond += cost.price;
                 }
                 if (totalCostPerSecond == 0) {
                     return FOREVER_MS;
                 }
-                return mAgent.getBalanceLocked(userId, pkgName) * 1000 / totalCostPerSecond;
+                final long minBalance = Math.min(
+                        mAgent.getBalanceLocked(userId, pkgName),
+                        mScribe.getRemainingConsumableNarcsLocked());
+                return minBalance * 1000 / totalCostPerSecond;
             }
         }
 
@@ -1085,10 +1066,20 @@
 
         private void updateEconomicPolicy() {
             synchronized (mLock) {
+                final long initialLimit =
+                        mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit();
+                final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit();
                 mCompleteEconomicPolicy.tearDown();
                 mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this);
                 if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
                     mCompleteEconomicPolicy.setup();
+                    if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()
+                            || hardLimit
+                            != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) {
+                        // Reset the consumption limit since several factors may have changed.
+                        mScribe.setConsumptionLimitLocked(
+                                mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
+                    }
                     mAgent.onPricingChangedLocked();
                 }
             }
@@ -1110,18 +1101,23 @@
             pw.print("Current battery level: ");
             pw.println(mCurrentBatteryLevel);
 
-            final long maxCirculation = getMaxCirculationLocked();
-            pw.print("Max circulation (current/satiated): ");
-            pw.print(narcToString(maxCirculation));
+            final long consumptionLimit = getConsumptionLimitLocked();
+            pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
+            pw.print(narcToString(consumptionLimit));
             pw.print("/");
-            pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
+            pw.print(narcToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
+            pw.print("/");
+            pw.println(narcToString(mScribe.getSatiatedConsumptionLimitLocked()));
 
-            final long currentCirculation = mScribe.getNarcsInCirculationLocked();
-            pw.print("Current GDP: ");
-            pw.print(narcToString(currentCirculation));
+            final long remainingConsumable = mScribe.getRemainingConsumableNarcsLocked();
+            pw.print("Goods remaining: ");
+            pw.print(narcToString(remainingConsumable));
             pw.print(" (");
-            pw.print(String.format("%.2f", 100f * currentCirculation / maxCirculation));
-            pw.println("% of current max)");
+            pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
+            pw.println("% of current limit)");
+
+            pw.print("Device wealth: ");
+            pw.println(narcToString(mScribe.getNarcsInCirculationForLoggingLocked()));
 
             pw.println();
             pw.print("Exempted apps", mExemptedApps);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 1f8ce26..0eddd22 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -38,7 +38,8 @@
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP;
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
 import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -79,7 +80,8 @@
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
 import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
-import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
+import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
 import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
 import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
@@ -145,7 +147,8 @@
     private long mMinSatiatedBalanceExempted;
     private long mMinSatiatedBalanceOther;
     private long mMaxSatiatedBalance;
-    private long mMaxSatiatedCirculation;
+    private long mInitialSatiatedConsumptionLimit;
+    private long mHardSatiatedConsumptionLimit;
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
     private final InternalResourceService mInternalResourceService;
@@ -181,8 +184,13 @@
     }
 
     @Override
-    long getMaxSatiatedCirculation() {
-        return mMaxSatiatedCirculation;
+    long getInitialSatiatedConsumptionLimit() {
+        return mInitialSatiatedConsumptionLimit;
+    }
+
+    @Override
+    long getHardSatiatedConsumptionLimit() {
+        return mHardSatiatedConsumptionLimit;
     }
 
     @NonNull
@@ -221,8 +229,11 @@
                         DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
         mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
                 DEFAULT_JS_MAX_SATIATED_BALANCE));
-        mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_JS_MAX_CIRCULATION,
-                DEFAULT_JS_MAX_CIRCULATION));
+        mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+                KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT));
+        mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
+                arcToNarc(mParser.getInt(
+                        KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT)));
 
         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
                 arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
@@ -332,7 +343,11 @@
         pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
         pw.decreaseIndent();
         pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
-        pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+        pw.print("Consumption limits: [");
+        pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+        pw.print(", ");
+        pw.print(narcToString(mHardSatiatedConsumptionLimit));
+        pw.println("]");
 
         pw.println();
         pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index f4917ad..dfdc20a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -41,14 +41,16 @@
         @Nullable
         public final String tag;
         public final long delta;
+        public final long ctp;
 
         Transaction(long startTimeMs, long endTimeMs,
-                int eventId, @Nullable String tag, long delta) {
+                int eventId, @Nullable String tag, long delta, long ctp) {
             this.startTimeMs = startTimeMs;
             this.endTimeMs = endTimeMs;
             this.eventId = eventId;
             this.tag = tag;
             this.delta = delta;
+            this.ctp = ctp;
         }
     }
 
@@ -144,7 +146,10 @@
                 pw.print(")");
             }
             pw.print(" --> ");
-            pw.println(narcToString(transaction.delta));
+            pw.print(narcToString(transaction.delta));
+            pw.print(" (ctp=");
+            pw.print(narcToString(transaction.ctp));
+            pw.println(")");
         }
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 86968ef..8662110 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -73,6 +73,7 @@
     private static final String XML_TAG_TRANSACTION = "transaction";
     private static final String XML_TAG_USER = "user";
 
+    private static final String XML_ATTR_CTP = "ctp";
     private static final String XML_ATTR_DELTA = "delta";
     private static final String XML_ATTR_EVENT_ID = "eventId";
     private static final String XML_ATTR_TAG = "tag";
@@ -83,6 +84,8 @@
     private static final String XML_ATTR_USER_ID = "userId";
     private static final String XML_ATTR_VERSION = "version";
     private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
+    private static final String XML_ATTR_REMAINING_CONSUMABLE_NARCS = "remainingConsumableNarcs";
+    private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
 
     /** Version of the file schema. */
     private static final int STATE_FILE_VERSION = 0;
@@ -95,7 +98,9 @@
     @GuardedBy("mIrs.getLock()")
     private long mLastReclamationTime;
     @GuardedBy("mIrs.getLock()")
-    private long mNarcsInCirculation;
+    private long mSatiatedConsumptionLimit;
+    @GuardedBy("mIrs.getLock()")
+    private long mRemainingConsumableNarcs;
     @GuardedBy("mIrs.getLock()")
     private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
 
@@ -117,10 +122,10 @@
     }
 
     @GuardedBy("mIrs.getLock()")
-    void adjustNarcsInCirculationLocked(long delta) {
+    void adjustRemainingConsumableNarcsLocked(long delta) {
         if (delta != 0) {
             // No point doing any work if the change is 0.
-            mNarcsInCirculation += delta;
+            mRemainingConsumableNarcs += delta;
             postWrite();
         }
     }
@@ -132,6 +137,11 @@
     }
 
     @GuardedBy("mIrs.getLock()")
+    long getSatiatedConsumptionLimitLocked() {
+        return mSatiatedConsumptionLimit;
+    }
+
+    @GuardedBy("mIrs.getLock()")
     long getLastReclamationTimeLocked() {
         return mLastReclamationTime;
     }
@@ -153,19 +163,37 @@
         return mLedgers;
     }
 
-    /** Returns the total amount of narcs currently allocated to apps. */
+    /**
+     * Returns the sum of credits granted to all apps on the system. This is expensive so don't
+     * call it for normal operation.
+     */
     @GuardedBy("mIrs.getLock()")
-    long getNarcsInCirculationLocked() {
-        return mNarcsInCirculation;
+    long getNarcsInCirculationForLoggingLocked() {
+        long sum = 0;
+        for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
+            for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
+                sum += mLedgers.valueAt(uIdx, pIdx).getCurrentBalance();
+            }
+        }
+        return sum;
+    }
+
+    /** Returns the total amount of narcs that remain to be consumed. */
+    @GuardedBy("mIrs.getLock()")
+    long getRemainingConsumableNarcsLocked() {
+        return mRemainingConsumableNarcs;
     }
 
     @GuardedBy("mIrs.getLock()")
     void loadFromDiskLocked() {
         mLedgers.clear();
-        mNarcsInCirculation = 0;
         if (!recordExists()) {
+            mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked();
+            mRemainingConsumableNarcs = mIrs.getConsumptionLimitLocked();
             return;
         }
+        mSatiatedConsumptionLimit = 0;
+        mRemainingConsumableNarcs = 0;
 
         final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
         final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
@@ -222,6 +250,13 @@
                     case XML_TAG_HIGH_LEVEL_STATE:
                         mLastReclamationTime =
                                 parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME);
+                        mSatiatedConsumptionLimit =
+                                parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
+                                        mIrs.getInitialSatiatedConsumptionLimitLocked());
+                        final long consumptionLimit = mIrs.getConsumptionLimitLocked();
+                        mRemainingConsumableNarcs = Math.min(consumptionLimit,
+                                parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+                                        consumptionLimit));
                         break;
                     case XML_TAG_USER:
                         earliestEndTime = Math.min(earliestEndTime,
@@ -249,6 +284,18 @@
     }
 
     @GuardedBy("mIrs.getLock()")
+    void setConsumptionLimitLocked(long limit) {
+        if (mRemainingConsumableNarcs > limit) {
+            mRemainingConsumableNarcs = limit;
+        } else if (limit > mSatiatedConsumptionLimit) {
+            final long diff = mSatiatedConsumptionLimit - mRemainingConsumableNarcs;
+            mRemainingConsumableNarcs = (limit - diff);
+        }
+        mSatiatedConsumptionLimit = limit;
+        postWrite();
+    }
+
+    @GuardedBy("mIrs.getLock()")
     void setLastReclamationTimeLocked(long time) {
         mLastReclamationTime = time;
         postWrite();
@@ -259,7 +306,8 @@
         TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
         TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
         mLedgers.clear();
-        mNarcsInCirculation = 0;
+        mRemainingConsumableNarcs = 0;
+        mSatiatedConsumptionLimit = 0;
         mLastReclamationTime = 0;
     }
 
@@ -339,13 +387,14 @@
             final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME);
             final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
             final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
+            final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP);
             if (endTime <= endTimeCutoff) {
                 if (DEBUG) {
                     Slog.d(TAG, "Skipping event because it's too old.");
                 }
                 continue;
             }
-            transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta));
+            transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp));
         }
 
         if (!isInstalled) {
@@ -395,7 +444,6 @@
                 final Ledger ledger = ledgerData.second;
                 if (ledger != null) {
                     mLedgers.add(curUser, ledgerData.first, ledger);
-                    mNarcsInCirculation += Math.max(0, ledger.getCurrentBalance());
                     final Ledger.Transaction transaction = ledger.getEarliestTransaction();
                     if (transaction != null) {
                         earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs);
@@ -442,6 +490,9 @@
 
                 out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
                 out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
+                out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
+                out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+                        mRemainingConsumableNarcs);
                 out.endTag(null, XML_TAG_HIGH_LEVEL_STATE);
 
                 for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
@@ -505,6 +556,7 @@
             out.attribute(null, XML_ATTR_TAG, transaction.tag);
         }
         out.attributeLong(null, XML_ATTR_DELTA, transaction.delta);
+        out.attributeLong(null, XML_ATTR_CTP, transaction.ctp);
         out.endTag(null, XML_TAG_TRANSACTION);
     }
 
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
deleted file mode 100644
index 1a710a98b..0000000
--- a/apex/media/Android.bp
+++ /dev/null
@@ -1,35 +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 {
-    default_visibility: [
-        ":__subpackages__",
-        "//frameworks/av/apex",
-        "//frameworks/av/apex/testing",
-    ],
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-sdk {
-    name: "media-module-sdk",
-    bootclasspath_fragments: ["com.android.media-bootclasspath-fragment"],
-    systemserverclasspath_fragments: ["com.android.media-systemserverclasspath-fragment"],
-    java_sdk_libs: [
-        "framework-media",
-    ],
-}
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
deleted file mode 100644
index 2c5965c..0000000
--- a/apex/media/OWNERS
+++ /dev/null
@@ -1,12 +0,0 @@
-# Bug component: 1344
-hdmoon@google.com
-jinpark@google.com
-klhyun@google.com
-lnilsson@google.com
-sungsoo@google.com
-
-# go/android-fwk-media-solutions for info on areas of ownership.
-include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
-
-# media reliability team packages/delivers the media mainline builds.
-include platform/frameworks/av:/media/janitors/reliability_mainline_OWNERS
diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp
deleted file mode 100644
index 545a0cd..0000000
--- a/apex/media/aidl/Android.bp
+++ /dev/null
@@ -1,44 +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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-filegroup {
-    name: "stable-media-aidl-srcs",
-    srcs: ["stable/**/*.aidl"],
-    path: "stable",
-}
-
-filegroup {
-    name: "private-media-aidl-srcs",
-    srcs: ["private/**/I*.aidl"],
-    path: "private",
-}
-
-filegroup {
-    name: "media-aidl-srcs",
-    srcs: [
-        ":private-media-aidl-srcs",
-        ":stable-media-aidl-srcs",
-    ],
-}
diff --git a/apex/media/aidl/private/android/media/Controller2Link.aidl b/apex/media/aidl/private/android/media/Controller2Link.aidl
deleted file mode 100644
index 64edafc..0000000
--- a/apex/media/aidl/private/android/media/Controller2Link.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-parcelable Controller2Link;
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
deleted file mode 100644
index e1c89e9..0000000
--- a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl
+++ /dev/null
@@ -1,34 +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.media;
-
-import android.media.Session2Token;
-import android.media.IMediaCommunicationServiceCallback;
-import android.media.MediaParceledListSlice;
-import android.view.KeyEvent;
-
-/** {@hide} */
-interface IMediaCommunicationService {
-    void notifySession2Created(in Session2Token sessionToken);
-    boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
-    MediaParceledListSlice getSession2Tokens(int userId);
-
-   void dispatchMediaKeyEvent(String packageName, in KeyEvent keyEvent, boolean asSystemService);
-
-    void registerCallback(IMediaCommunicationServiceCallback callback, String packageName);
-    void unregisterCallback(IMediaCommunicationServiceCallback callback);
-}
-
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
deleted file mode 100644
index e347ebf..0000000
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright 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.media;
-
-import android.media.Session2Token;
-import android.media.MediaParceledListSlice;
-
-/** {@hide} */
-oneway interface IMediaCommunicationServiceCallback {
-    void onSession2Created(in Session2Token token);
-    void onSession2Changed(in MediaParceledListSlice tokens);
-}
-
diff --git a/apex/media/aidl/private/android/media/IMediaController2.aidl b/apex/media/aidl/private/android/media/IMediaController2.aidl
deleted file mode 100644
index 42c6e70..0000000
--- a/apex/media/aidl/private/android/media/IMediaController2.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.media.Session2Command;
-
-/**
- * Interface from MediaSession2 to MediaController2.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from session to make session owner(s) frozen.
- * @hide
- */
- // Code for AML only
-oneway interface IMediaController2 {
-    void notifyConnected(int seq, in Bundle connectionResult) = 0;
-    void notifyDisconnected(int seq) = 1;
-    void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2;
-    void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
-            in ResultReceiver resultReceiver) = 3;
-    void cancelSessionCommand(int seq) = 4;
-    // Next Id : 5
-}
diff --git a/apex/media/aidl/private/android/media/IMediaSession2.aidl b/apex/media/aidl/private/android/media/IMediaSession2.aidl
deleted file mode 100644
index 26e717b..0000000
--- a/apex/media/aidl/private/android/media/IMediaSession2.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.media.Controller2Link;
-import android.media.Session2Command;
-
-/**
- * Interface from MediaController2 to MediaSession2.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from session to make session owner(s) frozen.
- * @hide
- */
- // Code for AML only
-oneway interface IMediaSession2 {
-    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
-    void disconnect(in Controller2Link caller, int seq) = 1;
-    void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand,
-            in Bundle args, in ResultReceiver resultReceiver) = 2;
-    void cancelSessionCommand(in Controller2Link caller, int seq) = 3;
-    // Next Id : 4
-}
diff --git a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl b/apex/media/aidl/private/android/media/IMediaSession2Service.aidl
deleted file mode 100644
index 10ac1be..0000000
--- a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Bundle;
-import android.media.Controller2Link;
-
-/**
- * Interface from MediaController2 to MediaSession2Service.
- * <p>
- * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
- * and holds calls from controller to make controller owner(s) frozen.
- * @hide
- */
-oneway interface IMediaSession2Service {
-    void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0;
-    // Next Id : 1
-}
diff --git a/apex/media/aidl/private/android/media/Session2Command.aidl b/apex/media/aidl/private/android/media/Session2Command.aidl
deleted file mode 100644
index 43a7b12..0000000
--- a/apex/media/aidl/private/android/media/Session2Command.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-parcelable Session2Command;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
deleted file mode 100644
index 2c2af28..0000000
--- a/apex/media/framework/Android.bp
+++ /dev/null
@@ -1,167 +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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-java_library {
-    name: "updatable-media",
-
-    srcs: [
-        ":updatable-media-srcs",
-    ],
-
-    permitted_packages: [
-        "android.media",
-    ],
-
-    optimize: {
-        enabled: true,
-        shrink: true,
-        proguard_flags_files: ["updatable-media-proguard.flags"],
-    },
-
-    installable: true,
-
-    sdk_version: "module_current",
-    libs: [
-        "androidx.annotation_annotation",
-        "framework-annotations-lib",
-    ],
-    static_libs: [
-        "exoplayer2-extractor",
-        "mediatranscoding_aidl_interface-java",
-        "modules-annotation-minsdk",
-        "modules-utils-build",
-    ],
-    jarjar_rules: "jarjar_rules.txt",
-
-    plugins: ["java_api_finder"],
-
-    hostdex: true, // for hiddenapi check
-    apex_available: [
-        "com.android.media",
-        "test_com.android.media",
-    ],
-    min_sdk_version: "29",
-    lint: {
-        strict_updatability_linting: true,
-    },
-    visibility: [
-        "//frameworks/av/apex:__subpackages__",
-        "//frameworks/base/apex/media/service",
-        "//frameworks/base/api", // For framework-all
-    ],
-}
-
-filegroup {
-    name: "updatable-media-srcs",
-    srcs: [
-        "java/android/media/MediaFrameworkInitializer.java",
-        ":media-aidl-srcs",
-        ":mediaparceledlistslice-java-srcs",
-        ":mediaparser-srcs",
-        ":mediasession2-java-srcs",
-        ":mediatranscoding-srcs",
-    ],
-    visibility: ["//frameworks/base"],
-}
-
-filegroup {
-    name: "mediasession2-java-srcs",
-    srcs: [
-        "java/android/media/Controller2Link.java",
-        "java/android/media/MediaConstants.java",
-        "java/android/media/MediaController2.java",
-        "java/android/media/MediaSession2.java",
-        "java/android/media/MediaSession2Service.java",
-        "java/android/media/Session2Command.java",
-        "java/android/media/Session2CommandGroup.java",
-        "java/android/media/Session2Link.java",
-        "java/android/media/Session2Token.java",
-        "java/android/media/MediaCommunicationManager.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediaparceledlistslice-java-srcs",
-    srcs: [
-        "java/android/media/MediaParceledListSlice.java",
-        "java/android/media/BaseMediaParceledListSlice.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediaparser-srcs",
-    srcs: [
-        "java/android/media/MediaParser.java",
-    ],
-    path: "java",
-}
-
-filegroup {
-    name: "mediatranscoding-srcs",
-    srcs: [
-        "java/android/media/ApplicationMediaCapabilities.java",
-        "java/android/media/MediaFeature.java",
-        "java/android/media/MediaTranscodingManager.java",
-    ],
-    path: "java",
-}
-
-java_sdk_library {
-    name: "framework-media",
-    defaults: ["framework-module-defaults"],
-
-    // This is only used to define the APIs for updatable-media.
-    api_only: true,
-
-    srcs: [
-        ":updatable-media-srcs",
-    ],
-
-    impl_library_visibility: ["//frameworks/av/apex:__subpackages__"],
-}
-
-cc_library_shared {
-    name: "libmediaparser-jni",
-    srcs: [
-        "jni/android_media_MediaParserJNI.cpp",
-    ],
-    header_libs: ["jni_headers"],
-    shared_libs: [
-        "libandroid",
-        "liblog",
-        "libmediametrics",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-        "-Wunreachable-code",
-        "-Wunused",
-    ],
-    apex_available: [
-        "com.android.media",
-    ],
-    min_sdk_version: "29",
-}
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
deleted file mode 100644
index 3d21914..0000000
--- a/apex/media/framework/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsMediaParserTestCases"
-    },
-    {
-      "name": "CtsMediaParserHostTestCases"
-    }
-  ]
-}
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
deleted file mode 100644
index b7d7ed8..0000000
--- a/apex/media/framework/api/current.txt
+++ /dev/null
@@ -1,267 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public final class ApplicationMediaCapabilities implements android.os.Parcelable {
-    method @NonNull public static android.media.ApplicationMediaCapabilities createFromXml(@NonNull org.xmlpull.v1.XmlPullParser);
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes();
-    method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
-    method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes();
-    method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes();
-    method public boolean isFormatSpecified(@NonNull String);
-    method public boolean isHdrTypeSupported(@NonNull String);
-    method public boolean isVideoMimeTypeSupported(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
-  }
-
-  public static final class ApplicationMediaCapabilities.Builder {
-    ctor public ApplicationMediaCapabilities.Builder();
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedHdrType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedVideoMimeType(@NonNull String);
-    method @NonNull public android.media.ApplicationMediaCapabilities build();
-  }
-
-  public class MediaCommunicationManager {
-    method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
-    method @IntRange(from=1) public int getVersion();
-  }
-
-  public class MediaController2 implements java.lang.AutoCloseable {
-    method public void cancelSessionCommand(@NonNull Object);
-    method public void close();
-    method @Nullable public android.media.Session2Token getConnectedToken();
-    method public boolean isPlaybackActive();
-    method @NonNull public Object sendSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public static final class MediaController2.Builder {
-    ctor public MediaController2.Builder(@NonNull android.content.Context, @NonNull android.media.Session2Token);
-    method @NonNull public android.media.MediaController2 build();
-    method @NonNull public android.media.MediaController2.Builder setConnectionHints(@NonNull android.os.Bundle);
-    method @NonNull public android.media.MediaController2.Builder setControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaController2.ControllerCallback);
-  }
-
-  public abstract static class MediaController2.ControllerCallback {
-    ctor public MediaController2.ControllerCallback();
-    method public void onCommandResult(@NonNull android.media.MediaController2, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
-    method public void onConnected(@NonNull android.media.MediaController2, @NonNull android.media.Session2CommandGroup);
-    method public void onDisconnected(@NonNull android.media.MediaController2);
-    method public void onPlaybackActiveChanged(@NonNull android.media.MediaController2, boolean);
-    method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaController2, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public final class MediaFeature {
-    ctor public MediaFeature();
-  }
-
-  public static final class MediaFeature.HdrType {
-    field public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
-    field public static final String HDR10 = "android.media.feature.hdr.hdr10";
-    field public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
-    field public static final String HLG = "android.media.feature.hdr.hlg";
-  }
-
-  public final class MediaParser {
-    method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException;
-    method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
-    method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
-    method @NonNull public android.media.metrics.LogSessionId getLogSessionId();
-    method @NonNull public String getParserName();
-    method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
-    method public void release();
-    method public void seek(@NonNull android.media.MediaParser.SeekPoint);
-    method public void setLogSessionId(@NonNull android.media.metrics.LogSessionId);
-    method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object);
-    method public boolean supportsParameter(@NonNull String);
-    field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "android.media.mediaparser.adts.enableCbrSeeking";
-    field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "android.media.mediaparser.amr.enableCbrSeeking";
-    field public static final String PARAMETER_FLAC_DISABLE_ID3 = "android.media.mediaparser.flac.disableId3";
-    field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "android.media.mediaparser.matroska.disableCuesSeeking";
-    field public static final String PARAMETER_MP3_DISABLE_ID3 = "android.media.mediaparser.mp3.disableId3";
-    field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "android.media.mediaparser.mp3.enableCbrSeeking";
-    field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "android.media.mediaparser.mp3.enableIndexSeeking";
-    field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "android.media.mediaparser.mp4.ignoreEditLists";
-    field public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = "android.media.mediaparser.mp4.ignoreTfdtBox";
-    field public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
-    field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
-    field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "android.media.mediaparser.ts.ignoreDetectAccessUnits";
-    field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
-    field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "android.media.mediaparser.ts.ignoreAacStream";
-    field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "android.media.mediaparser.ts.ignoreAvcStream";
-    field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "android.media.mediaparser.ts.ignoreSpliceInfoStream";
-    field public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
-    field public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser";
-    field public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser";
-    field public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser";
-    field public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser";
-    field public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser";
-    field public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser";
-    field public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
-    field public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
-    field public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser";
-    field public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
-    field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
-    field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
-    field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
-    field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
-    field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
-    field public static final int SAMPLE_FLAG_DECODE_ONLY = -2147483648; // 0x80000000
-    field public static final int SAMPLE_FLAG_ENCRYPTED = 1073741824; // 0x40000000
-    field public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 268435456; // 0x10000000
-    field public static final int SAMPLE_FLAG_KEY_FRAME = 1; // 0x1
-    field public static final int SAMPLE_FLAG_LAST_SAMPLE = 536870912; // 0x20000000
-  }
-
-  public static interface MediaParser.InputReader {
-    method public long getLength();
-    method public long getPosition();
-    method public int read(@NonNull byte[], int, int) throws java.io.IOException;
-  }
-
-  public static interface MediaParser.OutputConsumer {
-    method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
-    method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException;
-    method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap);
-    method public void onTrackCountFound(int);
-    method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
-  }
-
-  public static final class MediaParser.ParsingException extends java.io.IOException {
-  }
-
-  public static final class MediaParser.SeekMap {
-    method public long getDurationMicros();
-    method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
-    method public boolean isSeekable();
-    field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
-  }
-
-  public static final class MediaParser.SeekPoint {
-    field @NonNull public static final android.media.MediaParser.SeekPoint START;
-    field public final long position;
-    field public final long timeMicros;
-  }
-
-  public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
-    method public void seekToPosition(long);
-  }
-
-  public static final class MediaParser.TrackData {
-    field @Nullable public final android.media.DrmInitData drmInitData;
-    field @NonNull public final android.media.MediaFormat mediaFormat;
-  }
-
-  public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException {
-  }
-
-  public class MediaSession2 implements java.lang.AutoCloseable {
-    method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-    method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object);
-    method public void close();
-    method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers();
-    method @NonNull public String getId();
-    method @NonNull public android.media.Session2Token getToken();
-    method public boolean isPlaybackActive();
-    method @NonNull public Object sendSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-    method public void setPlaybackActive(boolean);
-  }
-
-  public static final class MediaSession2.Builder {
-    ctor public MediaSession2.Builder(@NonNull android.content.Context);
-    method @NonNull public android.media.MediaSession2 build();
-    method @NonNull public android.media.MediaSession2.Builder setExtras(@NonNull android.os.Bundle);
-    method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String);
-    method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent);
-    method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback);
-  }
-
-  public static final class MediaSession2.ControllerInfo {
-    method @NonNull public android.os.Bundle getConnectionHints();
-    method @NonNull public String getPackageName();
-    method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo();
-    method public int getUid();
-  }
-
-  public abstract static class MediaSession2.SessionCallback {
-    ctor public MediaSession2.SessionCallback();
-    method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result);
-    method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo);
-    method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
-  }
-
-  public abstract class MediaSession2Service extends android.app.Service {
-    ctor public MediaSession2Service();
-    method public final void addSession(@NonNull android.media.MediaSession2);
-    method @NonNull public final java.util.List<android.media.MediaSession2> getSessions();
-    method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @Nullable public abstract android.media.MediaSession2 onGetSession(@NonNull android.media.MediaSession2.ControllerInfo);
-    method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
-    method public final void removeSession(@NonNull android.media.MediaSession2);
-    field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
-  }
-
-  public static class MediaSession2Service.MediaNotification {
-    ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification);
-    method @NonNull public android.app.Notification getNotification();
-    method public int getNotificationId();
-  }
-
-  public final class Session2Command implements android.os.Parcelable {
-    ctor public Session2Command(int);
-    ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle);
-    method public int describeContents();
-    method public int getCommandCode();
-    method @Nullable public String getCustomAction();
-    method @Nullable public android.os.Bundle getCustomExtras();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR;
-  }
-
-  public static final class Session2Command.Result {
-    ctor public Session2Command.Result(int, @Nullable android.os.Bundle);
-    method public int getResultCode();
-    method @Nullable public android.os.Bundle getResultData();
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
-    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
-    field public static final int RESULT_SUCCESS = 0; // 0x0
-  }
-
-  public final class Session2CommandGroup implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.Set<android.media.Session2Command> getCommands();
-    method public boolean hasCommand(@NonNull android.media.Session2Command);
-    method public boolean hasCommand(int);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR;
-  }
-
-  public static final class Session2CommandGroup.Builder {
-    ctor public Session2CommandGroup.Builder();
-    ctor public Session2CommandGroup.Builder(@NonNull android.media.Session2CommandGroup);
-    method @NonNull public android.media.Session2CommandGroup.Builder addCommand(@NonNull android.media.Session2Command);
-    method @NonNull public android.media.Session2CommandGroup build();
-    method @NonNull public android.media.Session2CommandGroup.Builder removeCommand(@NonNull android.media.Session2Command);
-  }
-
-  public final class Session2Token implements android.os.Parcelable {
-    ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName);
-    method public int describeContents();
-    method @NonNull public android.os.Bundle getExtras();
-    method @NonNull public String getPackageName();
-    method @Nullable public String getServiceName();
-    method public int getType();
-    method public int getUid();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR;
-    field public static final int TYPE_SESSION = 0; // 0x0
-    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
-  }
-
-}
-
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
deleted file mode 100644
index 7317f14..0000000
--- a/apex/media/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public class MediaCommunicationManager {
-    method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback);
-    method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback);
-  }
-
-  public static interface MediaCommunicationManager.SessionCallback {
-    method public default void onSession2TokenCreated(@NonNull android.media.Session2Token);
-    method public default void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>);
-  }
-
-  public class MediaFrameworkInitializer {
-    method public static void registerServiceWrappers();
-    method public static void setMediaServiceManager(@NonNull android.media.MediaServiceManager);
-  }
-
-  @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
-    ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>);
-    method @Deprecated public int describeContents();
-    method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
-    method @Deprecated public java.util.List<T> getList();
-    method @Deprecated public void setInlineCountLimit(int);
-    method @Deprecated public void writeToParcel(android.os.Parcel, int);
-    field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
-  }
-
-}
-
diff --git a/apex/media/framework/api/module-lib-removed.txt b/apex/media/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/api/removed.txt b/apex/media/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
deleted file mode 100644
index 6eea769..0000000
--- a/apex/media/framework/api/system-current.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-// Signature format: 2.0
-package android.media {
-
-  public final class MediaTranscodingManager {
-    method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener);
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener {
-    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession);
-  }
-
-  public abstract static class MediaTranscodingManager.TranscodingRequest {
-    method public int getClientPid();
-    method public int getClientUid();
-    method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
-    method @NonNull public android.net.Uri getDestinationUri();
-    method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor();
-    method @NonNull public android.net.Uri getSourceUri();
-  }
-
-  public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver {
-    ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
-    method @Nullable public android.media.MediaFormat resolveVideoFormat();
-    method public boolean shouldTranscode();
-  }
-
-  public static final class MediaTranscodingManager.TranscodingSession {
-    method public boolean addClientUid(int);
-    method public void cancel();
-    method @NonNull public java.util.List<java.lang.Integer> getClientUids();
-    method public int getErrorCode();
-    method @IntRange(from=0, to=100) public int getProgress();
-    method public int getResult();
-    method public int getSessionId();
-    method public int getStatus();
-    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener);
-    field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
-    field public static final int ERROR_NONE = 0; // 0x0
-    field public static final int ERROR_SERVICE_DIED = 2; // 0x2
-    field public static final int RESULT_CANCELED = 4; // 0x4
-    field public static final int RESULT_ERROR = 3; // 0x3
-    field public static final int RESULT_NONE = 1; // 0x1
-    field public static final int RESULT_SUCCESS = 2; // 0x2
-    field public static final int STATUS_FINISHED = 3; // 0x3
-    field public static final int STATUS_PAUSED = 4; // 0x4
-    field public static final int STATUS_PENDING = 1; // 0x1
-    field public static final int STATUS_RUNNING = 2; // 0x2
-  }
-
-  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener {
-    method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int);
-  }
-
-  public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest {
-    method @NonNull public android.media.MediaFormat getVideoTrackFormat();
-  }
-
-  public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder {
-    ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-  }
-
-}
-
diff --git a/apex/media/framework/api/system-removed.txt b/apex/media/framework/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
deleted file mode 100644
index 91489dc..0000000
--- a/apex/media/framework/jarjar_rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-rule com.android.modules.** android.media.internal.@1
-rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
deleted file mode 100644
index 97fa0ec..0000000
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import com.android.modules.annotation.MinSdk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
- for handling newer video codec format and media features.
-
- <p>
- Android 12 introduces Compatible media transcoding feature.  See
- <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. By default, Android assumes apps can support playback of all
- media formats. Apps that would like to request that media be transcoded into a more compatible
- format should declare their media capabilities in a media_capabilities.xml resource file and add it
- as a property tag in the AndroidManifest.xml file. Here is a example:
- <pre>
- {@code
- <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
-     <format android:name="HEVC" supported="true"/>
-     <format android:name="HDR10" supported="false"/>
-     <format android:name="HDR10Plus" supported="false"/>
- </media-capabilities>
- }
- </pre>
- The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
- represent an application's media capabilities in order to determine whether modern media files need
- to be transcoded for that application.
- </p>
-
- <p>
- ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- provided by applications at runtime like this override the default manifest capabilities for that
- media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
- through the builder class {@link ApplicationMediaCapabilities.Builder}
-
- <h3> Video Codec Support</h3>
- <p>
- Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- for newer format with this class as they are assumed to support older format like h.264.
-
- <h3>Capability of handling HDR(high dynamic range) video</h3>
- <p>
- There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- application will only need to specify individual types they supported.
- */
-@MinSdk(Build.VERSION_CODES.S)
-public final class ApplicationMediaCapabilities implements Parcelable {
-    private static final String TAG = "ApplicationMediaCapabilities";
-
-    /** List of supported video codec mime types. */
-    private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
-
-    /** List of unsupported video codec mime types. */
-    private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>();
-
-    /** List of supported hdr types. */
-    private Set<String> mSupportedHdrTypes = new HashSet<>();
-
-    /** List of unsupported hdr types. */
-    private Set<String> mUnsupportedHdrTypes = new HashSet<>();
-
-    private boolean mIsSlowMotionSupported = false;
-
-    private ApplicationMediaCapabilities(Builder b) {
-        mSupportedVideoMimeTypes.addAll(b.getSupportedVideoMimeTypes());
-        mUnsupportedVideoMimeTypes.addAll(b.getUnsupportedVideoMimeTypes());
-        mSupportedHdrTypes.addAll(b.getSupportedHdrTypes());
-        mUnsupportedHdrTypes.addAll(b.getUnsupportedHdrTypes());
-        mIsSlowMotionSupported = b.mIsSlowMotionSupported;
-    }
-
-    /**
-     * Query if a video codec format is supported by the application.
-     * <p>
-     * If the application has not specified supporting the format or not, this will return false.
-     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
-     *
-     * @param videoMime The mime type of the video codec format. Must be the one used in
-     * {@link MediaFormat#KEY_MIME}.
-     * @return true if application supports the video codec format, false otherwise.
-     */
-    public boolean isVideoMimeTypeSupported(
-            @NonNull String videoMime) {
-        if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Query if a HDR type is supported by the application.
-     * <p>
-     * If the application has not specified supporting the format or not, this will return false.
-     * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
-     *
-     * @param hdrType The type of the HDR format.
-     * @return true if application supports the HDR format, false otherwise.
-     */
-    public boolean isHdrTypeSupported(
-            @NonNull @MediaFeature.MediaHdrType String hdrType) {
-        if (mSupportedHdrTypes.contains(hdrType)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Query if a format is specified by the application.
-     * <p>
-     * The format could be either the video format or the hdr format.
-     *
-     * @param format The name of the format.
-     * @return true if application specifies the format, false otherwise.
-     */
-    public boolean isFormatSpecified(@NonNull String format) {
-        if (mSupportedVideoMimeTypes.contains(format) || mUnsupportedVideoMimeTypes.contains(format)
-                || mSupportedHdrTypes.contains(format) || mUnsupportedHdrTypes.contains(format)) {
-            return true;
-
-        }
-        return false;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // Write out the supported video mime types.
-        dest.writeInt(mSupportedVideoMimeTypes.size());
-        for (String cap : mSupportedVideoMimeTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the unsupported video mime types.
-        dest.writeInt(mUnsupportedVideoMimeTypes.size());
-        for (String cap : mUnsupportedVideoMimeTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the supported hdr types.
-        dest.writeInt(mSupportedHdrTypes.size());
-        for (String cap : mSupportedHdrTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the unsupported hdr types.
-        dest.writeInt(mUnsupportedHdrTypes.size());
-        for (String cap : mUnsupportedHdrTypes) {
-            dest.writeString(cap);
-        }
-        // Write out the supported slow motion.
-        dest.writeBoolean(mIsSlowMotionSupported);
-    }
-
-    @Override
-    public String toString() {
-        String caps = new String(
-                "Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString());
-        caps += "Unsupported Video MimeTypes: " + mUnsupportedVideoMimeTypes.toString();
-        caps += "Supported HDR types: " + mSupportedHdrTypes.toString();
-        caps += "Unsupported HDR types: " + mUnsupportedHdrTypes.toString();
-        caps += "Supported slow motion: " + mIsSlowMotionSupported;
-        return caps;
-    }
-
-    @NonNull
-    public static final Creator<ApplicationMediaCapabilities> CREATOR =
-            new Creator<ApplicationMediaCapabilities>() {
-                public ApplicationMediaCapabilities createFromParcel(Parcel in) {
-                    ApplicationMediaCapabilities.Builder builder =
-                            new ApplicationMediaCapabilities.Builder();
-
-                    // Parse supported video codec mime types.
-                    int count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addSupportedVideoMimeType(in.readString());
-                    }
-
-                    // Parse unsupported video codec mime types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addUnsupportedVideoMimeType(in.readString());
-                    }
-
-                    // Parse supported hdr types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addSupportedHdrType(in.readString());
-                    }
-
-                    // Parse unsupported hdr types.
-                    count = in.readInt();
-                    for (int readCount = 0; readCount < count; ++readCount) {
-                        builder.addUnsupportedHdrType(in.readString());
-                    }
-
-                    boolean supported = in.readBoolean();
-                    builder.setSlowMotionSupported(supported);
-
-                    return builder.build();
-                }
-
-                public ApplicationMediaCapabilities[] newArray(int size) {
-                    return new ApplicationMediaCapabilities[size];
-                }
-            };
-
-    /**
-     * Query the video codec mime types supported by the application.
-     * @return List of supported video codec mime types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getSupportedVideoMimeTypes() {
-        return new ArrayList<>(mSupportedVideoMimeTypes);
-    }
-
-    /**
-     * Query the video codec mime types that are not supported by the application.
-     * @return List of unsupported video codec mime types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getUnsupportedVideoMimeTypes() {
-        return new ArrayList<>(mUnsupportedVideoMimeTypes);
-    }
-
-    /**
-     * Query all hdr types that are supported by the application.
-     * @return List of supported hdr types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getSupportedHdrTypes() {
-        return new ArrayList<>(mSupportedHdrTypes);
-    }
-
-    /**
-     * Query all hdr types that are not supported by the application.
-     * @return List of unsupported hdr types. The list will be empty if there are none.
-     */
-    @NonNull
-    public List<String> getUnsupportedHdrTypes()  {
-        return new ArrayList<>(mUnsupportedHdrTypes);
-    }
-
-    /**
-     * Whether handling of slow-motion video is supported
-     * @hide
-     */
-    public boolean isSlowMotionSupported() {
-        return mIsSlowMotionSupported;
-    }
-
-    /**
-     * Creates {@link ApplicationMediaCapabilities} from an xml.
-     *
-     * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
-     * <p> Here is an example:
-     *
-     * <pre>
-     * {@code
-     * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
-     *     <format android:name="HEVC" supported="true"/>
-     *     <format android:name="HDR10" supported="false"/>
-     *     <format android:name="HDR10Plus" supported="false"/>
-     * </media-capabilities>
-     * }
-     * </pre>
-     * <p>
-     *
-     * @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
-     * @return An ApplicationMediaCapabilities object.
-     * @throws UnsupportedOperationException if the capabilities in xml config are invalid or
-     * incompatible.
-     */
-    // TODO: Add developer.android.com link for the format of the xml.
-    @NonNull
-    public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
-        ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
-        builder.parseXml(xmlParser);
-        return builder.build();
-    }
-
-    /**
-     * Builder class for {@link ApplicationMediaCapabilities} objects.
-     * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder
-     * could be created from an existing ApplicationMediaCapabilities object, from a xml file or
-     * MediaCodecList.
-     * //TODO(hkuang): Add xml parsing support to the builder.
-     */
-    public final static class Builder {
-        /** List of supported video codec mime types. */
-        private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
-
-        /** List of supported hdr types. */
-        private Set<String> mSupportedHdrTypes = new HashSet<>();
-
-        /** List of unsupported video codec mime types. */
-        private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>();
-
-        /** List of unsupported hdr types. */
-        private Set<String> mUnsupportedHdrTypes = new HashSet<>();
-
-        private boolean mIsSlowMotionSupported = false;
-
-        /* Map to save the format read from the xml. */
-        private Map<String, Boolean> mFormatSupportedMap =  new HashMap<String, Boolean>();
-
-        /**
-         * Constructs a new Builder with all the supports default to false.
-         */
-        public Builder() {
-        }
-
-        private void parseXml(@NonNull XmlPullParser xmlParser)
-                throws UnsupportedOperationException {
-            if (xmlParser == null) {
-                throw new IllegalArgumentException("XmlParser must not be null");
-            }
-
-            try {
-                while (xmlParser.next() != XmlPullParser.START_TAG) {
-                    continue;
-                }
-
-                // Validates the tag is "media-capabilities".
-                if (!xmlParser.getName().equals("media-capabilities")) {
-                    throw new UnsupportedOperationException("Invalid tag");
-                }
-
-                xmlParser.next();
-                while (xmlParser.getEventType() != XmlPullParser.END_TAG) {
-                    while (xmlParser.getEventType() != XmlPullParser.START_TAG) {
-                        if (xmlParser.getEventType() == XmlPullParser.END_DOCUMENT) {
-                            return;
-                        }
-                        xmlParser.next();
-                    }
-
-                    // Validates the tag is "format".
-                    if (xmlParser.getName().equals("format")) {
-                        parseFormatTag(xmlParser);
-                    } else {
-                        throw new UnsupportedOperationException("Invalid tag");
-                    }
-                    while (xmlParser.getEventType() != XmlPullParser.END_TAG) {
-                        xmlParser.next();
-                    }
-                    xmlParser.next();
-                }
-            } catch (XmlPullParserException xppe) {
-                throw new UnsupportedOperationException("Ill-formatted xml file");
-            } catch (java.io.IOException ioe) {
-                throw new UnsupportedOperationException("Unable to read xml file");
-            }
-        }
-
-        private void parseFormatTag(XmlPullParser xmlParser) {
-            String name = null;
-            String supported = null;
-            for (int i = 0; i < xmlParser.getAttributeCount(); i++) {
-                String attrName = xmlParser.getAttributeName(i);
-                if (attrName.equals("name")) {
-                    name = xmlParser.getAttributeValue(i);
-                } else if (attrName.equals("supported")) {
-                    supported = xmlParser.getAttributeValue(i);
-                } else {
-                    throw new UnsupportedOperationException("Invalid attribute name " + attrName);
-                }
-            }
-
-            if (name != null && supported != null) {
-                if (!supported.equals("true") && !supported.equals("false")) {
-                    throw new UnsupportedOperationException(
-                            ("Supported value must be either true or false"));
-                }
-                boolean isSupported = Boolean.parseBoolean(supported);
-
-                // Check if the format is already found before.
-                if (mFormatSupportedMap.get(name) != null && mFormatSupportedMap.get(name)
-                        != isSupported) {
-                    throw new UnsupportedOperationException(
-                            "Format: " + name + " has conflict supported value");
-                }
-
-                switch (name) {
-                    case "HEVC":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
-                        }
-                        break;
-                    case "VP9":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9);
-                        }
-                        break;
-                    case "AV1":
-                        if (isSupported) {
-                            mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
-                        } else {
-                            mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1);
-                        }
-                        break;
-                    case "HDR10":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10);
-                        }
-                        break;
-                    case "HDR10Plus":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS);
-                        }
-                        break;
-                    case "Dolby-Vision":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION);
-                        }
-                        break;
-                    case "HLG":
-                        if (isSupported) {
-                            mSupportedHdrTypes.add(MediaFeature.HdrType.HLG);
-                        } else {
-                            mUnsupportedHdrTypes.add(MediaFeature.HdrType.HLG);
-                        }
-                        break;
-                    case "SlowMotion":
-                        mIsSlowMotionSupported = isSupported;
-                        break;
-                    default:
-                        Log.w(TAG, "Invalid format name " + name);
-                }
-                // Save the name and isSupported into the map for validate later.
-                mFormatSupportedMap.put(name, isSupported);
-            } else {
-                throw new UnsupportedOperationException(
-                        "Format name and supported must both be specified");
-            }
-        }
-
-        /**
-         * Builds a {@link ApplicationMediaCapabilities} object.
-         *
-         * @return a new {@link ApplicationMediaCapabilities} instance successfully initialized
-         * with all the parameters set on this <code>Builder</code>.
-         * @throws UnsupportedOperationException if the parameters set on the
-         *                                       <code>Builder</code> were incompatible, or if they
-         *                                       are not supported by the
-         *                                       device.
-         */
-        @NonNull
-        public ApplicationMediaCapabilities build() {
-            Log.d(TAG,
-                    "Building ApplicationMediaCapabilities with: (Supported HDR: "
-                            + mSupportedHdrTypes.toString() + " Unsupported HDR: "
-                            + mUnsupportedHdrTypes.toString() + ") (Supported Codec: "
-                            + " " + mSupportedVideoMimeTypes.toString() + " Unsupported Codec:"
-                            + mUnsupportedVideoMimeTypes.toString() + ") "
-                            + mIsSlowMotionSupported);
-
-            // If hdr is supported, application must also support hevc.
-            if (!mSupportedHdrTypes.isEmpty() && !mSupportedVideoMimeTypes.contains(
-                    MediaFormat.MIMETYPE_VIDEO_HEVC)) {
-                throw new UnsupportedOperationException("Only support HEVC mime type");
-            }
-            return new ApplicationMediaCapabilities(this);
-        }
-
-        /**
-         * Adds a supported video codec mime type.
-         *
-         * @param codecMime Supported codec mime types. Must be one of the mime type defined
-         *                  in {@link MediaFormat}.
-         * @throws IllegalArgumentException if mime type is not valid.
-         */
-        @NonNull
-        public Builder addSupportedVideoMimeType(
-                @NonNull String codecMime) {
-            mSupportedVideoMimeTypes.add(codecMime);
-            return this;
-        }
-
-        private List<String> getSupportedVideoMimeTypes() {
-            return new ArrayList<>(mSupportedVideoMimeTypes);
-        }
-
-        private boolean isValidVideoCodecMimeType(@NonNull String codecMime) {
-            if (!codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)
-                    && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9)
-                    && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) {
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Adds an unsupported video codec mime type.
-         *
-         * @param codecMime Unsupported codec mime type. Must be one of the mime type defined
-         *                  in {@link MediaFormat}.
-         * @throws IllegalArgumentException if mime type is not valid.
-         */
-        @NonNull
-        public Builder addUnsupportedVideoMimeType(
-                @NonNull String codecMime) {
-            if (!isValidVideoCodecMimeType(codecMime)) {
-                throw new IllegalArgumentException("Invalid codec mime type: " + codecMime);
-            }
-            mUnsupportedVideoMimeTypes.add(codecMime);
-            return this;
-        }
-
-        private List<String> getUnsupportedVideoMimeTypes() {
-            return new ArrayList<>(mUnsupportedVideoMimeTypes);
-        }
-
-        /**
-         * Adds a supported hdr type.
-         *
-         * @param hdrType Supported hdr type. Must be one of the String defined in
-         *                {@link MediaFeature.HdrType}.
-         * @throws IllegalArgumentException if hdrType is not valid.
-         */
-        @NonNull
-        public Builder addSupportedHdrType(
-                @NonNull @MediaFeature.MediaHdrType String hdrType) {
-            if (!isValidVideoCodecHdrType(hdrType)) {
-                throw new IllegalArgumentException("Invalid hdr type: " + hdrType);
-            }
-            mSupportedHdrTypes.add(hdrType);
-            return this;
-        }
-
-        private List<String> getSupportedHdrTypes() {
-            return new ArrayList<>(mSupportedHdrTypes);
-        }
-
-        private boolean isValidVideoCodecHdrType(@NonNull String hdrType) {
-            if (!hdrType.equals(MediaFeature.HdrType.DOLBY_VISION)
-                    && !hdrType.equals(MediaFeature.HdrType.HDR10)
-                    && !hdrType.equals(MediaFeature.HdrType.HDR10_PLUS)
-                    && !hdrType.equals(MediaFeature.HdrType.HLG)) {
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Adds an unsupported hdr type.
-         *
-         * @param hdrType Unsupported hdr type. Must be one of the String defined in
-         *                {@link MediaFeature.HdrType}.
-         * @throws IllegalArgumentException if hdrType is not valid.
-         */
-        @NonNull
-        public Builder addUnsupportedHdrType(
-                @NonNull @MediaFeature.MediaHdrType String hdrType) {
-            if (!isValidVideoCodecHdrType(hdrType)) {
-                throw new IllegalArgumentException("Invalid hdr type: " + hdrType);
-            }
-            mUnsupportedHdrTypes.add(hdrType);
-            return this;
-        }
-
-        private List<String> getUnsupportedHdrTypes() {
-            return new ArrayList<>(mUnsupportedHdrTypes);
-        }
-
-        /**
-         * Sets whether slow-motion video is supported.
-         * If an application indicates support for slow-motion, it is application's responsibility
-         * to parse the slow-motion videos using their own parser or using support library.
-         * @see android.media.MediaFormat#KEY_SLOW_MOTION_MARKERS
-         * @hide
-         */
-        @NonNull
-        public Builder setSlowMotionSupported(boolean slowMotionSupported) {
-            mIsSlowMotionSupported = slowMotionSupported;
-            return this;
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
deleted file mode 100644
index 915f3f6a3..0000000
--- a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
+++ /dev/null
@@ -1,215 +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.media;
-
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a copied version of BaseParceledListSlice in framework with hidden API usages
- * removed.
- *
- * Transfer a large list of Parcelable objects across an IPC.  Splits into
- * multiple transactions if needed.
- *
- * Caveat: for efficiency and security, all elements must be the same concrete type.
- * In order to avoid writing the class name of each object, we must ensure that
- * each object is the same type, or else unparceling then reparceling the data may yield
- * a different result if the class name encoded in the Parcelable is a Base type.
- * See b/17671747.
- *
- * @hide
- */
-abstract class BaseMediaParceledListSlice<T> implements Parcelable {
-    private static String TAG = "BaseMediaParceledListSlice";
-    private static final boolean DEBUG = false;
-
-    /*
-     * TODO get this number from somewhere else. For now set it to a quarter of
-     * the 1MB limit.
-     */
-    // private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
-    private static final int MAX_IPC_SIZE = 64 * 1024;
-
-    private final List<T> mList;
-
-    private int mInlineCountLimit = Integer.MAX_VALUE;
-
-    public BaseMediaParceledListSlice(List<T> list) {
-        mList = list;
-    }
-
-    @SuppressWarnings("unchecked")
-    BaseMediaParceledListSlice(Parcel p, ClassLoader loader) {
-        final int N = p.readInt();
-        mList = new ArrayList<T>(N);
-        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
-        if (N <= 0) {
-            return;
-        }
-
-        Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
-        Class<?> listElementClass = null;
-
-        int i = 0;
-        while (i < N) {
-            if (p.readInt() == 0) {
-                break;
-            }
-
-            final T parcelable = readCreator(creator, p, loader);
-            if (listElementClass == null) {
-                listElementClass = parcelable.getClass();
-            } else {
-                verifySameType(listElementClass, parcelable.getClass());
-            }
-
-            mList.add(parcelable);
-
-            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
-            i++;
-        }
-        if (i >= N) {
-            return;
-        }
-        final IBinder retriever = p.readStrongBinder();
-        while (i < N) {
-            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
-            Parcel data = Parcel.obtain();
-            Parcel reply = Parcel.obtain();
-            data.writeInt(i);
-            try {
-                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
-                return;
-            }
-            while (i < N && reply.readInt() != 0) {
-                final T parcelable = readCreator(creator, reply, loader);
-                verifySameType(listElementClass, parcelable.getClass());
-
-                mList.add(parcelable);
-
-                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
-                i++;
-            }
-            reply.recycle();
-            data.recycle();
-        }
-    }
-
-    private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
-        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
-            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
-                    (Parcelable.ClassLoaderCreator<?>) creator;
-            return (T) classLoaderCreator.createFromParcel(p, loader);
-        }
-        return (T) creator.createFromParcel(p);
-    }
-
-    private static void verifySameType(final Class<?> expected, final Class<?> actual) {
-        if (!actual.equals(expected)) {
-            throw new IllegalArgumentException("Can't unparcel type "
-                    + (actual == null ? null : actual.getName()) + " in list of type "
-                    + (expected == null ? null : expected.getName()));
-        }
-    }
-
-    public List<T> getList() {
-        return mList;
-    }
-
-    /**
-     * Set a limit on the maximum number of entries in the array that will be included
-     * inline in the initial parcelling of this object.
-     */
-    public void setInlineCountLimit(int maxCount) {
-        mInlineCountLimit = maxCount;
-    }
-
-    /**
-     * Write this to another Parcel. Note that this discards the internal Parcel
-     * and should not be used anymore. This is so we can pass this to a Binder
-     * where we won't have a chance to call recycle on this.
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        final int N = mList.size();
-        final int callFlags = flags;
-        dest.writeInt(N);
-        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
-        if (N > 0) {
-            final Class<?> listElementClass = mList.get(0).getClass();
-            writeParcelableCreator(mList.get(0), dest);
-            int i = 0;
-            while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
-                dest.writeInt(1);
-
-                final T parcelable = mList.get(i);
-                verifySameType(listElementClass, parcelable.getClass());
-                writeElement(parcelable, dest, callFlags);
-
-                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
-                i++;
-            }
-            if (i < N) {
-                dest.writeInt(0);
-                Binder retriever = new Binder() {
-                    @Override
-                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
-                            throws RemoteException {
-                        if (code != FIRST_CALL_TRANSACTION) {
-                            return super.onTransact(code, data, reply, flags);
-                        }
-                        int i = data.readInt();
-                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
-                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
-                            reply.writeInt(1);
-
-                            final T parcelable = mList.get(i);
-                            verifySameType(listElementClass, parcelable.getClass());
-                            writeElement(parcelable, reply, callFlags);
-
-                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
-                            i++;
-                        }
-                        if (i < N) {
-                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
-                            reply.writeInt(0);
-                        }
-                        return true;
-                    }
-                };
-                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
-                dest.writeStrongBinder(retriever);
-            }
-        }
-    }
-
-    abstract void writeElement(T parcelable, Parcel reply, int callFlags);
-
-    abstract void writeParcelableCreator(T parcelable, Parcel dest);
-
-    abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
-}
diff --git a/apex/media/framework/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java
deleted file mode 100644
index 04af028..0000000
--- a/apex/media/framework/java/android/media/BufferingParams.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Structure for source buffering management params.
- *
- * Used by {@link MediaPlayer#getBufferingParams()} and
- * {@link MediaPlayer#setBufferingParams(BufferingParams)}
- * to control source buffering behavior.
- *
- * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
- * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
- * is playing back source). {@link BufferingParams} includes corresponding marks for each
- * stage of source buffering. The marks are time based (in milliseconds).
- *
- * <p>{@link MediaPlayer} source component has default marks which can be queried by
- * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
- * {@link MediaPlayer#setBufferingParams()}.
- * <ul>
- * <li><strong>initial buffering:</strong> initialMarkMs is used when
- * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
- * {@link MediaPlayer} is prepared. </li>
- * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
- * {@link MediaPlayer} is playing back content.
- * <ul>
- * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
- * to pause playback if cached data amount runs low. This internal mark varies based on
- * type of data source. </li>
- * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
- * resume playback if it has been paused due to low cached data amount. The internal mark
- * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
- * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
- * when to pause rebuffering. Apparently, this internal mark shall be no less than
- * resumePlaybackMarkMs. </li>
- * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
- * when to resume buffering. This internal mark varies based on type of data source. This
- * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
- * </li>
- * </ul> </li>
- * </ul>
- * <p>Users should use {@link Builder} to change {@link BufferingParams}.
- * @hide
- */
-public final class BufferingParams implements Parcelable {
-    private static final int BUFFERING_NO_MARK = -1;
-
-    // params
-    private int mInitialMarkMs = BUFFERING_NO_MARK;
-
-    private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
-
-    private BufferingParams() {
-    }
-
-    /**
-     * Return initial buffering mark in milliseconds.
-     * @return initial buffering mark in milliseconds
-     */
-    public int getInitialMarkMs() {
-        return mInitialMarkMs;
-    }
-
-    /**
-     * Return the mark in milliseconds for resuming playback.
-     * @return the mark for resuming playback in milliseconds
-     */
-    public int getResumePlaybackMarkMs() {
-        return mResumePlaybackMarkMs;
-    }
-
-    /**
-     * Builder class for {@link BufferingParams} objects.
-     * <p> Here is an example where <code>Builder</code> is used to define the
-     * {@link BufferingParams} to be used by a {@link MediaPlayer} instance:
-     *
-     * <pre class="prettyprint">
-     * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
-     * myParams = new BufferingParams.Builder(myParams)
-     *         .setInitialMarkMs(10000)
-     *         .setResumePlaybackMarkMs(15000)
-     *         .build();
-     * mediaplayer.setBufferingParams(myParams);
-     * </pre>
-     */
-    public static class Builder {
-        private int mInitialMarkMs = BUFFERING_NO_MARK;
-        private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
-
-        /**
-         * Constructs a new Builder with the defaults.
-         * By default, all marks are -1.
-         */
-        public Builder() {
-        }
-
-        /**
-         * Constructs a new Builder from a given {@link BufferingParams} instance
-         * @param bp the {@link BufferingParams} object whose data will be reused
-         * in the new Builder.
-         */
-        public Builder(BufferingParams bp) {
-            mInitialMarkMs = bp.mInitialMarkMs;
-            mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
-        }
-
-        /**
-         * Combines all of the fields that have been set and return a new
-         * {@link BufferingParams} object. <code>IllegalStateException</code> will be
-         * thrown if there is conflict between fields.
-         * @return a new {@link BufferingParams} object
-         */
-        public BufferingParams build() {
-            BufferingParams bp = new BufferingParams();
-            bp.mInitialMarkMs = mInitialMarkMs;
-            bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;
-
-            return bp;
-        }
-
-        /**
-         * Sets the time based mark in milliseconds for initial buffering.
-         * @param markMs time based mark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setInitialMarkMs(int markMs) {
-            mInitialMarkMs = markMs;
-            return this;
-        }
-
-        /**
-         * Sets the time based mark in milliseconds for resuming playback.
-         * @param markMs time based mark in milliseconds for resuming playback
-         * @return the same Builder instance.
-         */
-        public Builder setResumePlaybackMarkMs(int markMs) {
-            mResumePlaybackMarkMs = markMs;
-            return this;
-        }
-    }
-
-    private BufferingParams(Parcel in) {
-        mInitialMarkMs = in.readInt();
-        mResumePlaybackMarkMs = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BufferingParams> CREATOR =
-            new Parcelable.Creator<BufferingParams>() {
-                @Override
-                public BufferingParams createFromParcel(Parcel in) {
-                    return new BufferingParams(in);
-                }
-
-                @Override
-                public BufferingParams[] newArray(int size) {
-                    return new BufferingParams[size];
-                }
-            };
-
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mInitialMarkMs);
-        dest.writeInt(mResumePlaybackMarkMs);
-    }
-}
diff --git a/apex/media/framework/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java
deleted file mode 100644
index 8eefec7..0000000
--- a/apex/media/framework/java/android/media/Controller2Link.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-
-import java.util.Objects;
-
-/**
- * Handles incoming commands from {@link MediaSession2} to {@link MediaController2}.
- * @hide
- */
-// @SystemApi
-public final class Controller2Link implements Parcelable {
-    private static final String TAG = "Controller2Link";
-    private static final boolean DEBUG = MediaController2.DEBUG;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Controller2Link> CREATOR =
-            new Parcelable.Creator<Controller2Link>() {
-                @Override
-                public Controller2Link createFromParcel(Parcel in) {
-                    return new Controller2Link(in);
-                }
-
-                @Override
-                public Controller2Link[] newArray(int size) {
-                    return new Controller2Link[size];
-                }
-            };
-
-
-    private final MediaController2 mController;
-    private final IMediaController2 mIController;
-
-    public Controller2Link(MediaController2 controller) {
-        mController = controller;
-        mIController = new Controller2Stub();
-    }
-
-    Controller2Link(Parcel in) {
-        mController = null;
-        mIController = IMediaController2.Stub.asInterface(in.readStrongBinder());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mIController.asBinder());
-    }
-
-    @Override
-    public int hashCode() {
-        return mIController.asBinder().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Controller2Link)) {
-            return false;
-        }
-        Controller2Link other = (Controller2Link) obj;
-        return Objects.equals(mIController.asBinder(), other.mIController.asBinder());
-    }
-
-    /** Interface method for IMediaController2.notifyConnected */
-    public void notifyConnected(int seq, Bundle connectionResult) {
-        try {
-            mIController.notifyConnected(seq, connectionResult);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.notifyDisonnected */
-    public void notifyDisconnected(int seq) {
-        try {
-            mIController.notifyDisconnected(seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.notifyPlaybackActiveChanged */
-    public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
-        try {
-            mIController.notifyPlaybackActiveChanged(seq, playbackActive);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.sendSessionCommand */
-    public void sendSessionCommand(int seq, Session2Command command, Bundle args,
-            ResultReceiver resultReceiver) {
-        try {
-            mIController.sendSessionCommand(seq, command, args, resultReceiver);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaController2.cancelSessionCommand */
-    public void cancelSessionCommand(int seq) {
-        try {
-            mIController.cancelSessionCommand(seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Stub implementation for IMediaController2.notifyConnected */
-    public void onConnected(int seq, Bundle connectionResult) {
-        if (connectionResult == null) {
-            onDisconnected(seq);
-            return;
-        }
-        mController.onConnected(seq, connectionResult);
-    }
-
-    /** Stub implementation for IMediaController2.notifyDisonnected */
-    public void onDisconnected(int seq) {
-        mController.onDisconnected(seq);
-    }
-
-    /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */
-    public void onPlaybackActiveChanged(int seq, boolean playbackActive) {
-        mController.onPlaybackActiveChanged(seq, playbackActive);
-    }
-
-    /** Stub implementation for IMediaController2.sendSessionCommand */
-    public void onSessionCommand(int seq, Session2Command command, Bundle args,
-            ResultReceiver resultReceiver) {
-        mController.onSessionCommand(seq, command, args, resultReceiver);
-    }
-
-    /** Stub implementation for IMediaController2.cancelSessionCommand */
-    public void onCancelCommand(int seq) {
-        mController.onCancelCommand(seq);
-    }
-
-    private class Controller2Stub extends IMediaController2.Stub {
-        @Override
-        public void notifyConnected(int seq, Bundle connectionResult) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onConnected(seq, connectionResult);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifyDisconnected(int seq) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onDisconnected(seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void sendSessionCommand(int seq, Session2Command command, Bundle args,
-                ResultReceiver resultReceiver) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void cancelSessionCommand(int seq) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Controller2Link.this.onCancelCommand(seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java
deleted file mode 100644
index c297ecd..0000000
--- a/apex/media/framework/java/android/media/DataSourceCallback.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.media;
-
-import android.annotation.NonNull;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-/**
- * For supplying media data to the framework. Implement this if your app has
- * special requirements for the way media data is obtained.
- *
- * <p class="note">Methods of this interface may be called on multiple different
- * threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your DataSourceCallback are visible to future calls. This means
- * you don't need to do your own synchronization unless you're modifying the
- * DataSourceCallback from another thread while it's being used by the framework.</p>
- *
- * @hide
- */
-public abstract class DataSourceCallback implements Closeable {
-
-    public static final int END_OF_STREAM = -1;
-
-    /**
-     * Called to request data from the given position.
-     *
-     * Implementations should should write up to {@code size} bytes into
-     * {@code buffer}, and return the number of bytes written.
-     *
-     * Return {@code 0} if size is zero (thus no bytes are read).
-     *
-     * Return {@code -1} to indicate that end of stream is reached.
-     *
-     * @param position the position in the data source to read from.
-     * @param buffer the buffer to read the data into.
-     * @param offset the offset within buffer to read the data into.
-     * @param size the number of bytes to read.
-     * @throws IOException on fatal errors.
-     * @return the number of bytes read, or {@link #END_OF_STREAM} if end of stream is reached.
-     */
-    public abstract int readAt(long position, @NonNull byte[] buffer, int offset, int size)
-            throws IOException;
-
-    /**
-     * Called to get the size of the data source.
-     *
-     * @throws IOException on fatal errors
-     * @return the size of data source in bytes, or -1 if the size is unknown.
-     */
-    public abstract long getSize() throws IOException;
-}
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
deleted file mode 100644
index ef5552e..0000000
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ /dev/null
@@ -1,339 +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.media;
-
-import static android.Manifest.permission.MEDIA_CONTENT_CONTROL;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.content.Context;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.media.MediaBrowserService;
-import android.util.Log;
-import android.view.KeyEvent;
-
-import androidx.annotation.RequiresApi;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-
-/**
- * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s}
- * that applications have published to express their ongoing media playback state.
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
-public class MediaCommunicationManager {
-    private static final String TAG = "MediaCommunicationManager";
-
-    /**
-     * The manager version used from beginning.
-     */
-    private static final int VERSION_1 = 1;
-
-    /**
-     * Current manager version.
-     */
-    private static final int CURRENT_VERSION = VERSION_1;
-
-    private final Context mContext;
-    // Do not access directly use getService().
-    private IMediaCommunicationService mService;
-
-    private final Object mLock = new Object();
-    private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords =
-            new CopyOnWriteArrayList<>();
-
-    @GuardedBy("mLock")
-    private MediaCommunicationServiceCallbackStub mCallbackStub;
-
-    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
-    private MediaSessionManager mMediaSessionManager;
-
-    /**
-     * @hide
-     */
-    public MediaCommunicationManager(@NonNull Context context) {
-        if (!SdkLevel.isAtLeastS()) {
-            throw new UnsupportedOperationException("Android version must be S or greater.");
-        }
-        mContext = context;
-    }
-
-    /**
-     * Gets the version of this {@link MediaCommunicationManager}.
-     */
-    public @IntRange(from = 1) int getVersion() {
-        return CURRENT_VERSION;
-    }
-
-    /**
-     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
-     * created.
-     * @param token newly created session2 token
-     * @hide
-     */
-    public void notifySession2Created(@NonNull Session2Token token) {
-        Objects.requireNonNull(token, "token shouldn't be null");
-        if (token.getType() != Session2Token.TYPE_SESSION) {
-            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
-        }
-        try {
-            getService().notifySession2Created(token);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Checks whether the remote user is a trusted app.
-     * <p>
-     * An app is trusted if the app holds the
-     * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission or has an enabled
-     * notification listener.
-     *
-     * @param userInfo The remote user info from either
-     *            {@link MediaSession#getCurrentControllerInfo()} or
-     *            {@link MediaBrowserService#getCurrentBrowserInfo()}.
-     * @return {@code true} if the remote user is trusted or {@code false} otherwise.
-     * @hide
-     */
-    public boolean isTrustedForMediaControl(@NonNull MediaSessionManager.RemoteUserInfo userInfo) {
-        Objects.requireNonNull(userInfo, "userInfo shouldn't be null");
-        if (userInfo.getPackageName() == null) {
-            return false;
-        }
-        try {
-            return getService().isTrusted(
-                    userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot communicate with the service.", e);
-        }
-        return false;
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
-     * current user.
-     * <p>
-     * Although this API can be used without any restriction, each session owners can accept or
-     * reject your uses of {@link MediaSession2}.
-     *
-     * @return A list of {@link Session2Token}.
-     */
-    @NonNull
-    public List<Session2Token> getSession2Tokens() {
-        return getSession2Tokens(UserHandle.myUserId());
-    }
-
-    /**
-     * Adds a callback to be notified when the list of active sessions changes.
-     * <p>
-     * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be
-     * held by the calling app.
-     * </p>
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    @RequiresPermission(MEDIA_CONTENT_CONTROL)
-    public void registerSessionCallback(@CallbackExecutor @NonNull Executor executor,
-            @NonNull SessionCallback callback) {
-        Objects.requireNonNull(executor, "executor must not be null");
-        Objects.requireNonNull(callback, "callback must not be null");
-
-        if (!mTokenCallbackRecords.addIfAbsent(
-                new SessionCallbackRecord(executor, callback))) {
-            Log.w(TAG, "registerSession2TokenCallback: Ignoring the same callback");
-            return;
-        }
-        synchronized (mLock) {
-            if (mCallbackStub == null) {
-                MediaCommunicationServiceCallbackStub callbackStub =
-                        new MediaCommunicationServiceCallbackStub();
-                try {
-                    getService().registerCallback(callbackStub, mContext.getPackageName());
-                    mCallbackStub = callbackStub;
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Failed to register callback.", ex);
-                }
-            }
-        }
-    }
-
-    /**
-     * Stops receiving active sessions updates on the specified callback.
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public void unregisterSessionCallback(@NonNull SessionCallback callback) {
-        if (!mTokenCallbackRecords.remove(
-                new SessionCallbackRecord(null, callback))) {
-            Log.w(TAG, "unregisterSession2TokenCallback: Ignoring an unknown callback.");
-            return;
-        }
-        synchronized (mLock) {
-            if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) {
-                try {
-                    getService().unregisterCallback(mCallbackStub);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Failed to unregister callback.", ex);
-                }
-                mCallbackStub = null;
-            }
-        }
-    }
-
-    private IMediaCommunicationService getService() {
-        if (mService == null) {
-            mService = IMediaCommunicationService.Stub.asInterface(
-                    MediaFrameworkInitializer.getMediaServiceManager()
-                            .getMediaCommunicationServiceRegisterer()
-                            .get());
-        }
-        return mService;
-    }
-
-    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
-    private MediaSessionManager getMediaSessionManager() {
-        if (mMediaSessionManager == null) {
-            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
-        }
-        return mMediaSessionManager;
-    }
-
-    private List<Session2Token> getSession2Tokens(int userId) {
-        try {
-            MediaParceledListSlice slice = getService().getSession2Tokens(userId);
-            return slice == null ? Collections.emptyList() : slice.getList();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get session tokens", e);
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * Sends a media key event. The receiver will be selected automatically.
-     *
-     * @param keyEvent the key event to send
-     * @param asSystemService if {@code true}, the event sent to the session as if it was come from
-     *                        the system service instead of the app process.
-     * @hide
-     */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) {
-        Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
-
-        // When MCS handles this, caller is changed.
-        // TODO: remove this when MCS implementation is done.
-        if (!asSystemService) {
-            getMediaSessionManager().dispatchMediaKeyEvent(keyEvent, false);
-            return;
-        }
-
-        try {
-            getService().dispatchMediaKeyEvent(mContext.getPackageName(),
-                    keyEvent, asSystemService);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to send key event.", e);
-        }
-    }
-
-    /**
-     * Callback for listening to changes to the sessions.
-     * @see #registerSessionCallback(Executor, SessionCallback)
-     * @hide
-     */
-    @SystemApi(client = MODULE_LIBRARIES)
-    public interface SessionCallback {
-        /**
-         * Called when a new {@link MediaSession2 media session2} is created.
-         * @param token the newly created token
-         */
-        default void onSession2TokenCreated(@NonNull Session2Token token) {}
-
-        /**
-         * Called when {@link #getSession2Tokens() session tokens} are changed.
-         */
-        default void onSession2TokensChanged(@NonNull List<Session2Token> tokens) {}
-    }
-
-    private static final class SessionCallbackRecord {
-        public final Executor executor;
-        public final SessionCallback callback;
-
-        SessionCallbackRecord(Executor executor, SessionCallback callback) {
-            this.executor = executor;
-            this.callback = callback;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(callback);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (!(obj instanceof SessionCallbackRecord)) {
-                return false;
-            }
-            return Objects.equals(this.callback, ((SessionCallbackRecord) obj).callback);
-        }
-    }
-
-    class MediaCommunicationServiceCallbackStub extends IMediaCommunicationServiceCallback.Stub {
-        @Override
-        public void onSession2Created(Session2Token token) throws RemoteException {
-            for (SessionCallbackRecord record : mTokenCallbackRecords) {
-                record.executor.execute(() -> record.callback.onSession2TokenCreated(token));
-            }
-        }
-
-        @Override
-        public void onSession2Changed(MediaParceledListSlice tokens) throws RemoteException {
-            List<Session2Token> tokenList = tokens.getList();
-            for (SessionCallbackRecord record : mTokenCallbackRecords) {
-                record.executor.execute(() -> record.callback.onSession2TokensChanged(tokenList));
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java
deleted file mode 100644
index ce10889..0000000
--- a/apex/media/framework/java/android/media/MediaConstants.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-class MediaConstants {
-    // Bundle key for int
-    static final String KEY_PID = "android.media.key.PID";
-
-    // Bundle key for String
-    static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME";
-
-    // Bundle key for Parcelable
-    static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK";
-    static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS";
-    static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE";
-    static final String KEY_TOKEN_EXTRAS = "android.media.key.TOKEN_EXTRAS";
-    static final String KEY_CONNECTION_HINTS = "android.media.key.CONNECTION_HINTS";
-
-    private MediaConstants() {
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
deleted file mode 100644
index 159e8e5..0000000
--- a/apex/media/framework/java/android/media/MediaController2.java
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-import static android.media.MediaConstants.KEY_PACKAGE_NAME;
-import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2LINK;
-import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
-import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR;
-import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.TYPE_SESSION;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import java.util.concurrent.Executor;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- *
- * Allows an app to interact with an active {@link MediaSession2} or a
- * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
- * commands can be sent to the session.
- */
-public class MediaController2 implements AutoCloseable {
-    static final String TAG = "MediaController2";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final ControllerCallback mCallback;
-
-    private final IBinder.DeathRecipient mDeathRecipient = () -> close();
-    private final Context mContext;
-    private final Session2Token mSessionToken;
-    private final Executor mCallbackExecutor;
-    private final Controller2Link mControllerStub;
-    private final Handler mResultHandler;
-    private final SessionServiceConnection mServiceConnection;
-
-    private final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    private boolean mClosed;
-    //@GuardedBy("mLock")
-    private int mNextSeqNumber;
-    //@GuardedBy("mLock")
-    private Session2Link mSessionBinder;
-    //@GuardedBy("mLock")
-    private Session2CommandGroup mAllowedCommands;
-    //@GuardedBy("mLock")
-    private Session2Token mConnectedToken;
-    //@GuardedBy("mLock")
-    private ArrayMap<ResultReceiver, Integer> mPendingCommands;
-    //@GuardedBy("mLock")
-    private ArraySet<Integer> mRequestedCommandSeqNumbers;
-    //@GuardedBy("mLock")
-    private boolean mPlaybackActive;
-
-    /**
-     * Create a {@link MediaController2} from the {@link Session2Token}.
-     * This connects to the session and may wake up the service if it's not available.
-     *
-     * @param context context
-     * @param token token to connect to
-     * @param connectionHints a session-specific argument to send to the session when connecting.
-     *                        The contents of this bundle may affect the connection result.
-     * @param executor executor to run callbacks on.
-     * @param callback controller callback to receive changes in.
-     */
-    MediaController2(@NonNull Context context, @NonNull Session2Token token,
-            @NonNull Bundle connectionHints, @NonNull Executor executor,
-            @NonNull ControllerCallback callback) {
-        if (context == null) {
-            throw new IllegalArgumentException("context shouldn't be null");
-        }
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        mContext = context;
-        mSessionToken = token;
-        mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
-        mCallback = (callback == null) ? new ControllerCallback() {} : callback;
-        mControllerStub = new Controller2Link(this);
-        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
-        mResultHandler = new Handler(context.getMainLooper());
-
-        mNextSeqNumber = 0;
-        mPendingCommands = new ArrayMap<>();
-        mRequestedCommandSeqNumbers = new ArraySet<>();
-
-        boolean connectRequested;
-        if (token.getType() == TYPE_SESSION) {
-            mServiceConnection = null;
-            connectRequested = requestConnectToSession(connectionHints);
-        } else {
-            mServiceConnection = new SessionServiceConnection(connectionHints);
-            connectRequested = requestConnectToService();
-        }
-        if (!connectRequested) {
-            close();
-        }
-    }
-
-    @Override
-    public void close() {
-        synchronized (mLock) {
-            if (mClosed) {
-                // Already closed. Ignore rest of clean up code.
-                // Note: unbindService() throws IllegalArgumentException when it's called twice.
-                return;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "closing " + this);
-            }
-            mClosed = true;
-            if (mServiceConnection != null) {
-                // Note: This should be called even when the bindService() has returned false.
-                mContext.unbindService(mServiceConnection);
-            }
-            if (mSessionBinder != null) {
-                try {
-                    mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
-                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
-                } catch (RuntimeException e) {
-                    // No-op
-                }
-            }
-            mConnectedToken = null;
-            mPendingCommands.clear();
-            mRequestedCommandSeqNumbers.clear();
-            mCallbackExecutor.execute(() -> {
-                mCallback.onDisconnected(MediaController2.this);
-            });
-            mSessionBinder = null;
-        }
-    }
-
-    /**
-     * Returns {@link Session2Token} of the connected session.
-     * If it is not connected yet, it returns {@code null}.
-     * <p>
-     * This may differ with the {@link Session2Token} from the constructor. For example, if the
-     * controller is created with the token for {@link MediaSession2Service}, this would return
-     * token for the {@link MediaSession2} in the service.
-     *
-     * @return Session2Token of the connected session, or {@code null} if not connected
-     */
-    @Nullable
-    public Session2Token getConnectedToken() {
-        synchronized (mLock) {
-            return mConnectedToken;
-        }
-    }
-
-    /**
-     * Returns whether the session's playback is active.
-     *
-     * @return {@code true} if playback active. {@code false} otherwise.
-     * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean)
-     */
-    public boolean isPlaybackActive() {
-        synchronized (mLock) {
-            return mPlaybackActive;
-        }
-    }
-
-    /**
-     * Sends a session command to the session
-     * <p>
-     * @param command the session command
-     * @param args optional arguments
-     * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
-     *        when its result is received.
-     */
-    @NonNull
-    public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-
-        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                synchronized (mLock) {
-                    mPendingCommands.remove(this);
-                }
-                mCallbackExecutor.execute(() -> {
-                    mCallback.onCommandResult(MediaController2.this, this,
-                            command, new Session2Command.Result(resultCode, resultData));
-                });
-            }
-        };
-
-        synchronized (mLock) {
-            if (mSessionBinder != null) {
-                int seq = getNextSeqNumber();
-                mPendingCommands.put(resultReceiver, seq);
-                try {
-                    mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args,
-                            resultReceiver);
-                } catch (RuntimeException e)  {
-                    mPendingCommands.remove(resultReceiver);
-                    resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
-                }
-            }
-        }
-        return resultReceiver;
-    }
-
-    /**
-     * Cancels the session command previously sent.
-     *
-     * @param token the token which is returned from {@link #sendSessionCommand}.
-     */
-    public void cancelSessionCommand(@NonNull Object token) {
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        synchronized (mLock) {
-            if (mSessionBinder == null) return;
-            Integer seq = mPendingCommands.remove(token);
-            if (seq != null) {
-                mSessionBinder.cancelSessionCommand(mControllerStub, seq);
-            }
-        }
-    }
-
-    // Called by Controller2Link.onConnected
-    void onConnected(int seq, Bundle connectionResult) {
-        Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK);
-        Session2CommandGroup allowedCommands =
-                connectionResult.getParcelable(KEY_ALLOWED_COMMANDS);
-        boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE);
-
-        Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS);
-        if (tokenExtras == null) {
-            Log.w(TAG, "extras shouldn't be null.");
-            tokenExtras = Bundle.EMPTY;
-        } else if (MediaSession2.hasCustomParcelable(tokenExtras)) {
-            Log.w(TAG, "extras contain custom parcelable. Ignoring.");
-            tokenExtras = Bundle.EMPTY;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder
-                    + ", allowedCommands=" + allowedCommands);
-        }
-        if (sessionBinder == null || allowedCommands == null) {
-            // Connection rejected.
-            close();
-            return;
-        }
-        synchronized (mLock) {
-            mSessionBinder = sessionBinder;
-            mAllowedCommands = allowedCommands;
-            mPlaybackActive = playbackActive;
-
-            // Implementation for the local binder is no-op,
-            // so can be used without worrying about deadlock.
-            sessionBinder.linkToDeath(mDeathRecipient, 0);
-            mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION,
-                    mSessionToken.getPackageName(), sessionBinder, tokenExtras);
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onConnected(MediaController2.this, allowedCommands);
-        });
-    }
-
-    // Called by Controller2Link.onDisconnected
-    void onDisconnected(int seq) {
-        // close() will call mCallback.onDisconnected
-        close();
-    }
-
-    // Called by Controller2Link.onPlaybackActiveChanged
-    void onPlaybackActiveChanged(int seq, boolean playbackActive) {
-        synchronized (mLock) {
-            mPlaybackActive = playbackActive;
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive);
-        });
-    }
-
-    // Called by Controller2Link.onSessionCommand
-    void onSessionCommand(int seq, Session2Command command, Bundle args,
-            @Nullable ResultReceiver resultReceiver) {
-        synchronized (mLock) {
-            mRequestedCommandSeqNumbers.add(seq);
-        }
-        mCallbackExecutor.execute(() -> {
-            boolean isCanceled;
-            synchronized (mLock) {
-                isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
-            }
-            if (isCanceled) {
-                if (resultReceiver != null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                }
-                return;
-            }
-            Session2Command.Result result = mCallback.onSessionCommand(
-                    MediaController2.this, command, args);
-            if (resultReceiver != null) {
-                if (result == null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                } else {
-                    resultReceiver.send(result.getResultCode(), result.getResultData());
-                }
-            }
-        });
-    }
-
-    // Called by Controller2Link.onSessionCommand
-    void onCancelCommand(int seq) {
-        synchronized (mLock) {
-            mRequestedCommandSeqNumbers.remove(seq);
-        }
-    }
-
-    private int getNextSeqNumber() {
-        synchronized (mLock) {
-            return mNextSeqNumber++;
-        }
-    }
-
-    private Bundle createConnectionRequest(@NonNull Bundle connectionHints) {
-        Bundle connectionRequest = new Bundle();
-        connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName());
-        connectionRequest.putInt(KEY_PID, Process.myPid());
-        connectionRequest.putBundle(KEY_CONNECTION_HINTS, connectionHints);
-        return connectionRequest;
-    }
-
-    private boolean requestConnectToSession(@NonNull Bundle connectionHints) {
-        Session2Link sessionBinder = mSessionToken.getSessionLink();
-        Bundle connectionRequest = createConnectionRequest(connectionHints);
-        try {
-            sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
-        } catch (RuntimeException e) {
-            Log.w(TAG, "Failed to call connection request", e);
-            return false;
-        }
-        return true;
-    }
-
-    private boolean requestConnectToService() {
-        // Service. Needs to get fresh binder whenever connection is needed.
-        final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE);
-        intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName());
-
-        // Use bindService() instead of startForegroundService() to start session service for three
-        // reasons.
-        // 1. Prevent session service owner's stopSelf() from destroying service.
-        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
-        //    onDestroy() calls on the main thread even when onConnect() is running in another
-        //    thread.
-        // 2. Minimize APIs for developers to take care about.
-        //    With bindService(), developers only need to take care about Service.onBind()
-        //    but Service.onStartCommand() should be also taken care about with the
-        //    startForegroundService().
-        // 3. Future support for UI-less playback
-        //    If a service wants to keep running, it should be either foreground service or
-        //    bound service. But there had been request for the feature for system apps
-        //    and using bindService() will be better fit with it.
-        synchronized (mLock) {
-            boolean result = mContext.bindService(
-                    intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-            if (!result) {
-                Log.w(TAG, "bind to " + mSessionToken + " failed");
-                return false;
-            } else if (DEBUG) {
-                Log.d(TAG, "bind to " + mSessionToken + " succeeded");
-            }
-        }
-        return true;
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builder for {@link MediaController2}.
-     * <p>
-     * Any incoming event from the {@link MediaSession2} will be handled on the callback
-     * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default.
-     */
-    public static final class Builder {
-        private Context mContext;
-        private Session2Token mToken;
-        private Bundle mConnectionHints;
-        private Executor mCallbackExecutor;
-        private ControllerCallback mCallback;
-
-        /**
-         * Creates a builder for {@link MediaController2}.
-         *
-         * @param context context
-         * @param token token of the session to connect to
-         */
-        public Builder(@NonNull Context context, @NonNull Session2Token token) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            if (token == null) {
-                throw new IllegalArgumentException("token shouldn't be null");
-            }
-            mContext = context;
-            mToken = token;
-        }
-
-        /**
-         * Set the connection hints for the controller.
-         * <p>
-         * {@code connectionHints} is a session-specific argument to send to the session when
-         * connecting. The contents of this bundle may affect the connection result.
-         * <p>
-         * An {@link IllegalArgumentException} will be thrown if the bundle contains any
-         * non-framework Parcelable objects.
-         *
-         * @param connectionHints a bundle which contains the connection hints
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setConnectionHints(@NonNull Bundle connectionHints) {
-            if (connectionHints == null) {
-                throw new IllegalArgumentException("connectionHints shouldn't be null");
-            }
-            if (MediaSession2.hasCustomParcelable(connectionHints)) {
-                throw new IllegalArgumentException("connectionHints shouldn't contain any custom "
-                        + "parcelables");
-            }
-            mConnectionHints = new Bundle(connectionHints);
-            return this;
-        }
-
-        /**
-         * Set callback for the controller and its executor.
-         *
-         * @param executor callback executor
-         * @param callback session callback.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setControllerCallback(@NonNull Executor executor,
-                @NonNull ControllerCallback callback) {
-            if (executor == null) {
-                throw new IllegalArgumentException("executor shouldn't be null");
-            }
-            if (callback == null) {
-                throw new IllegalArgumentException("callback shouldn't be null");
-            }
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return this;
-        }
-
-        /**
-         * Build {@link MediaController2}.
-         *
-         * @return a new controller
-         */
-        @NonNull
-        public MediaController2 build() {
-            if (mCallbackExecutor == null) {
-                mCallbackExecutor = mContext.getMainExecutor();
-            }
-            if (mCallback == null) {
-                mCallback = new ControllerCallback() {};
-            }
-            if (mConnectionHints == null) {
-                mConnectionHints = Bundle.EMPTY;
-            }
-            return new MediaController2(
-                    mContext, mToken, mConnectionHints, mCallbackExecutor, mCallback);
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Interface for listening to change in activeness of the {@link MediaSession2}.
-     */
-    public abstract static class ControllerCallback {
-        /**
-         * Called when the controller is successfully connected to the session. The controller
-         * becomes available afterwards.
-         *
-         * @param controller the controller for this event
-         * @param allowedCommands commands that's allowed by the session.
-         */
-        public void onConnected(@NonNull MediaController2 controller,
-                @NonNull Session2CommandGroup allowedCommands) {}
-
-        /**
-         * Called when the session refuses the controller or the controller is disconnected from
-         * the session. The controller becomes unavailable afterwards and the callback wouldn't
-         * be called.
-         * <p>
-         * It will be also called after the {@link #close()}, so you can put clean up code here.
-         * You don't need to call {@link #close()} after this.
-         *
-         * @param controller the controller for this event
-         */
-        public void onDisconnected(@NonNull MediaController2 controller) {}
-
-        /**
-         * Called when the session's playback activeness is changed.
-         *
-         * @param controller the controller for this event
-         * @param playbackActive {@code true} if the session's playback is active.
-         *                       {@code false} otherwise.
-         * @see MediaController2#isPlaybackActive()
-         */
-        public void onPlaybackActiveChanged(@NonNull MediaController2 controller,
-                boolean playbackActive) {}
-
-        /**
-         * Called when the connected session sent a session command.
-         *
-         * @param controller the controller for this event
-         * @param command the session command
-         * @param args optional arguments
-         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
-         *         will be sent to the session.
-         */
-        @Nullable
-        public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
-                @NonNull Session2Command command, @Nullable Bundle args) {
-            return null;
-        }
-
-        /**
-         * Called when the command sent to the connected session is finished.
-         *
-         * @param controller the controller for this event
-         * @param token the token got from {@link MediaController2#sendSessionCommand}
-         * @param command the session command
-         * @param result the result of the session command
-         */
-        public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
-    }
-
-    // This will be called on the main thread.
-    private class SessionServiceConnection implements ServiceConnection {
-        private final Bundle mConnectionHints;
-
-        SessionServiceConnection(@Nullable Bundle connectionHints) {
-            mConnectionHints = connectionHints;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            // Note that it's always main-thread.
-            boolean connectRequested = false;
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "onServiceConnected " + name + " " + this);
-                }
-                if (!mSessionToken.getPackageName().equals(name.getPackageName())) {
-                    Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName()
-                            + " but is connected to " + name);
-                    return;
-                }
-                IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service);
-                if (iService == null) {
-                    Log.wtf(TAG, "Service interface is missing.");
-                    return;
-                }
-                Bundle connectionRequest = createConnectionRequest(mConnectionHints);
-                iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest);
-                connectRequested = true;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Service " + name + " has died prematurely", e);
-            } finally {
-                if (!connectRequested) {
-                    close();
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // Temporal lose of the binding because of the service crash. System will automatically
-            // rebind, so just no-op.
-            if (DEBUG) {
-                Log.w(TAG, "Session service " + name + " is disconnected.");
-            }
-            close();
-        }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            // Permanent lose of the binding because of the service package update or removed.
-            // This SessionServiceRecord will be removed accordingly, but forget session binder here
-            // for sure.
-            close();
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java
deleted file mode 100644
index 8d1b159..0000000
--- a/apex/media/framework/java/android/media/MediaFeature.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.StringDef;
-import android.os.Build;
-
-import com.android.modules.annotation.MinSdk;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * MediaFeature defines various media features, e.g. hdr type.
- */
-@MinSdk(Build.VERSION_CODES.S)
-public final class MediaFeature {
-     /**
-     * Defines tye type of HDR(high dynamic range) video.
-     */
-    public static final class HdrType {
-        private HdrType() {
-        }
-
-        /**
-         * HDR type for dolby-vision.
-         */
-        public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision";
-        /**
-         * HDR type for hdr10.
-         */
-        public static final String HDR10 = "android.media.feature.hdr.hdr10";
-        /**
-         * HDR type for hdr10+.
-         */
-        public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus";
-        /**
-         * HDR type for hlg.
-         */
-        public static final String HLG = "android.media.feature.hdr.hlg";
-    }
-
-    /** @hide */
-    @StringDef({
-            HdrType.DOLBY_VISION,
-            HdrType.HDR10,
-            HdrType.HDR10_PLUS,
-            HdrType.HLG,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MediaHdrType {
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
deleted file mode 100644
index d7ad97d..0000000
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ /dev/null
@@ -1,92 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.app.SystemServiceRegistry;
-import android.content.Context;
-import android.os.Build;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-/**
- * Class for performing registration for all media services on com.android.media apex.
- *
- * @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi(client = Client.MODULE_LIBRARIES)
-public class MediaFrameworkInitializer {
-    private MediaFrameworkInitializer() {
-    }
-
-    private static volatile MediaServiceManager sMediaServiceManager;
-
-    /**
-     * Sets an instance of {@link MediaServiceManager} that allows
-     * the media mainline module to register/obtain media binder services. This is called
-     * by the platform during the system initialization.
-     *
-     * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
-     * the media mainline module to register/obtain media binder services.
-     */
-    public static void setMediaServiceManager(
-            @NonNull MediaServiceManager mediaServiceManager) {
-        if (sMediaServiceManager != null) {
-            throw new IllegalStateException("setMediaServiceManager called twice!");
-        }
-
-        if (mediaServiceManager == null) {
-            throw new NullPointerException("mediaServiceManager is null!");
-        }
-
-        sMediaServiceManager = mediaServiceManager;
-    }
-
-    /** @hide */
-    public static MediaServiceManager getMediaServiceManager() {
-        return sMediaServiceManager;
-    }
-
-    /**
-     * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
-     * services to {@link Context}, so that {@link Context#getSystemService} can return them.
-     *
-     * @throws IllegalStateException if this is called from anywhere besides
-     * {@link SystemServiceRegistry}
-     */
-    public static void registerServiceWrappers() {
-        SystemServiceRegistry.registerContextAwareService(
-                Context.MEDIA_TRANSCODING_SERVICE,
-                MediaTranscodingManager.class,
-                context -> new MediaTranscodingManager(context)
-        );
-        if (SdkLevel.isAtLeastS()) {
-            SystemServiceRegistry.registerContextAwareService(
-                    Context.MEDIA_COMMUNICATION_SERVICE,
-                    MediaCommunicationManager.class,
-                    context -> new MediaCommunicationManager(context)
-            );
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
deleted file mode 100644
index fb36e80..0000000
--- a/apex/media/framework/java/android/media/MediaParceledListSlice.java
+++ /dev/null
@@ -1,107 +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.media;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import androidx.annotation.RequiresApi;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This is a copied version of MediaParceledListSlice in framework with hidden API usages removed,
- * and also with some lint error fixed.
- *
- * Transfer a large list of Parcelable objects across an IPC.  Splits into
- * multiple transactions if needed.
- *
- * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved
- *       to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API
- *       for moving classes step by step.
- *
- * @param <T> The type of the elements in the list.
- * @see BaseMediaParceledListSlice
- * @deprecated This is temporary marked as @SystemApi. Should be removed from the API surface.
- * @hide
- */
-@Deprecated
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-public final class MediaParceledListSlice<T extends Parcelable>
-        extends BaseMediaParceledListSlice<T> {
-    public MediaParceledListSlice(@NonNull List<T> list) {
-        super(list);
-    }
-
-    private MediaParceledListSlice(Parcel in, ClassLoader loader) {
-        super(in, loader);
-    }
-
-    @NonNull
-    public static <T extends Parcelable> MediaParceledListSlice<T> emptyList() {
-        return new MediaParceledListSlice<T>(Collections.<T> emptyList());
-    }
-
-    @Override
-    public int describeContents() {
-        int contents = 0;
-        final List<T> list = getList();
-        for (int i=0; i<list.size(); i++) {
-            contents |= list.get(i).describeContents();
-        }
-        return contents;
-    }
-
-    @Override
-    void writeElement(T parcelable, Parcel dest, int callFlags) {
-        parcelable.writeToParcel(dest, callFlags);
-    }
-
-    @Override
-    void writeParcelableCreator(T parcelable, Parcel dest) {
-        dest.writeParcelableCreator((Parcelable) parcelable);
-    }
-
-    @Override
-    Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
-        return from.readParcelableCreator(loader);
-    }
-
-    @NonNull
-    @SuppressWarnings("unchecked")
-    public static final Parcelable.ClassLoaderCreator<MediaParceledListSlice> CREATOR =
-            new Parcelable.ClassLoaderCreator<MediaParceledListSlice>() {
-        public MediaParceledListSlice createFromParcel(Parcel in) {
-            return new MediaParceledListSlice(in, null);
-        }
-
-        @Override
-        public MediaParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
-            return new MediaParceledListSlice(in, loader);
-        }
-
-        @Override
-        public MediaParceledListSlice[] newArray(int size) {
-            return new MediaParceledListSlice[size];
-        }
-    };
-}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
deleted file mode 100644
index b6f85c7..0000000
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ /dev/null
@@ -1,2293 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.media.MediaCodec.CryptoInfo;
-import android.media.metrics.LogSessionId;
-import android.os.Build;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.Format;
-import com.google.android.exoplayer2.ParserException;
-import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
-import com.google.android.exoplayer2.extractor.ChunkIndex;
-import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
-import com.google.android.exoplayer2.extractor.Extractor;
-import com.google.android.exoplayer2.extractor.ExtractorInput;
-import com.google.android.exoplayer2.extractor.ExtractorOutput;
-import com.google.android.exoplayer2.extractor.PositionHolder;
-import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints;
-import com.google.android.exoplayer2.extractor.TrackOutput;
-import com.google.android.exoplayer2.extractor.amr.AmrExtractor;
-import com.google.android.exoplayer2.extractor.flac.FlacExtractor;
-import com.google.android.exoplayer2.extractor.flv.FlvExtractor;
-import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
-import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
-import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
-import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
-import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
-import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
-import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
-import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
-import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
-import com.google.android.exoplayer2.extractor.ts.PsExtractor;
-import com.google.android.exoplayer2.extractor.ts.TsExtractor;
-import com.google.android.exoplayer2.extractor.wav.WavExtractor;
-import com.google.android.exoplayer2.upstream.DataReader;
-import com.google.android.exoplayer2.util.ParsableByteArray;
-import com.google.android.exoplayer2.util.TimestampAdjuster;
-import com.google.android.exoplayer2.util.Util;
-import com.google.android.exoplayer2.video.ColorInfo;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.function.Function;
-
-/**
- * Parses media container formats and extracts contained media samples and metadata.
- *
- * <p>This class provides access to a battery of low-level media container parsers. Each instance of
- * this class is associated to a specific media parser implementation which is suitable for
- * extraction from a specific media container format. The media parser implementation assignment
- * depends on the factory method (see {@link #create} and {@link #createByName}) used to create the
- * instance.
- *
- * <p>Users must implement the following to use this class.
- *
- * <ul>
- *   <li>{@link InputReader}: Provides the media container's bytes to parse.
- *   <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata.
- * </ul>
- *
- * <p>The following code snippet includes a usage example:
- *
- * <pre>
- * MyOutputConsumer myOutputConsumer = new MyOutputConsumer();
- * MyInputReader myInputReader = new MyInputReader("www.example.com");
- * MediaParser mediaParser = MediaParser.create(myOutputConsumer);
- *
- * while (mediaParser.advance(myInputReader)) {}
- *
- * mediaParser.release();
- * mediaParser = null;
- * </pre>
- *
- * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation
- * which extracts and publishes all video samples:
- *
- * <pre>
- * class VideoOutputConsumer implements MediaParser.OutputConsumer {
- *
- *     private byte[] sampleDataBuffer = new byte[4096];
- *     private byte[] discardedDataBuffer = new byte[4096];
- *     private int videoTrackIndex = -1;
- *     private int bytesWrittenCount = 0;
- *
- *     &#64;Override
- *     public void onSeekMapFound(int i, &#64;NonNull MediaFormat mediaFormat) {
- *       // Do nothing.
- *     }
- *
- *     &#64;Override
- *     public void onTrackDataFound(int i, &#64;NonNull TrackData trackData) {
- *       MediaFormat mediaFormat = trackData.mediaFormat;
- *       if (videoTrackIndex == -1 &amp;&amp;
- *           mediaFormat
- *               .getString(MediaFormat.KEY_MIME, &#47;* defaultValue= *&#47; "")
- *               .startsWith("video/")) {
- *         videoTrackIndex = i;
- *       }
- *     }
- *
- *     &#64;Override
- *     public void onSampleDataFound(int trackIndex, &#64;NonNull InputReader inputReader)
- *         throws IOException {
- *       int numberOfBytesToRead = (int) inputReader.getLength();
- *       if (videoTrackIndex != trackIndex) {
- *         // Discard contents.
- *         inputReader.read(
- *             discardedDataBuffer,
- *             &#47;* offset= *&#47; 0,
- *             Math.min(discardDataBuffer.length, numberOfBytesToRead));
- *       } else {
- *         ensureSpaceInBuffer(numberOfBytesToRead);
- *         int bytesRead = inputReader.read(
- *             sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
- *         bytesWrittenCount += bytesRead;
- *       }
- *     }
- *
- *     &#64;Override
- *     public void onSampleCompleted(
- *         int trackIndex,
- *         long timeMicros,
- *         int flags,
- *         int size,
- *         int offset,
- *         &#64;Nullable CryptoInfo cryptoData) {
- *       if (videoTrackIndex != trackIndex) {
- *         return; // It's not the video track. Ignore.
- *       }
- *       byte[] sampleData = new byte[size];
- *       int sampleStartOffset = bytesWrittenCount - size - offset;
- *       System.arraycopy(
- *           sampleDataBuffer,
- *           sampleStartOffset,
- *           sampleData,
- *           &#47;* destPos= *&#47; 0,
- *           size);
- *       // Place trailing bytes at the start of the buffer.
- *       System.arraycopy(
- *           sampleDataBuffer,
- *           bytesWrittenCount - offset,
- *           sampleDataBuffer,
- *           &#47;* destPos= *&#47; 0,
- *           &#47;* size= *&#47; offset);
- *       bytesWrittenCount = bytesWrittenCount - offset;
- *       publishSample(sampleData, timeMicros, flags);
- *     }
- *
- *    private void ensureSpaceInBuffer(int numberOfBytesToRead) {
- *      int requiredLength = bytesWrittenCount + numberOfBytesToRead;
- *      if (requiredLength &gt; sampleDataBuffer.length) {
- *        sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength);
- *      }
- *    }
- *
- *   }
- *
- * </pre>
- */
-@RequiresApi(Build.VERSION_CODES.R)
-public final class MediaParser {
-
-    /**
-     * Maps seek positions to {@link SeekPoint SeekPoints} in the stream.
-     *
-     * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
-     * playing media samples.
-     */
-    public static final class SeekMap {
-
-        /** Returned by {@link #getDurationMicros()} when the duration is unknown. */
-        public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
-
-        /**
-         * For each {@link #getSeekPoints} call, returns a single {@link SeekPoint} whose {@link
-         * SeekPoint#timeMicros} matches the requested timestamp, and whose {@link
-         * SeekPoint#position} is 0.
-         *
-         * @hide
-         */
-        public static final SeekMap DUMMY = new SeekMap(new DummyExoPlayerSeekMap());
-
-        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
-
-        private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mExoPlayerSeekMap = exoplayerSeekMap;
-        }
-
-        /** Returns whether seeking is supported. */
-        public boolean isSeekable() {
-            return mExoPlayerSeekMap.isSeekable();
-        }
-
-        /**
-         * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
-         * duration is unknown.
-         */
-        public long getDurationMicros() {
-            long durationUs = mExoPlayerSeekMap.getDurationUs();
-            return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION;
-        }
-
-        /**
-         * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
-         *
-         * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples
-         * with timestamp equal to or smaller than {@code timeMicros}.
-         *
-         * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples
-         * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for
-         * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice.
-         *
-         * @param timeMicros A seek time in microseconds.
-         * @return The corresponding {@link SeekPoint SeekPoints}.
-         */
-        @NonNull
-        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) {
-            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros);
-            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
-        }
-    }
-
-    /** Holds information associated with a track. */
-    public static final class TrackData {
-
-        /** Holds {@link MediaFormat} information for the track. */
-        @NonNull public final MediaFormat mediaFormat;
-
-        /**
-         * Holds {@link DrmInitData} necessary to acquire keys associated with the track, or null if
-         * the track has no encryption data.
-         */
-        @Nullable public final DrmInitData drmInitData;
-
-        private TrackData(MediaFormat mediaFormat, DrmInitData drmInitData) {
-            this.mediaFormat = mediaFormat;
-            this.drmInitData = drmInitData;
-        }
-    }
-
-    /** Defines a seek point in a media stream. */
-    public static final class SeekPoint {
-
-        /** A {@link SeekPoint} whose time and byte offset are both set to 0. */
-        @NonNull public static final SeekPoint START = new SeekPoint(0, 0);
-
-        /** The time of the seek point, in microseconds. */
-        public final long timeMicros;
-
-        /** The byte offset of the seek point. */
-        public final long position;
-
-        /**
-         * @param timeMicros The time of the seek point, in microseconds.
-         * @param position The byte offset of the seek point.
-         */
-        private SeekPoint(long timeMicros, long position) {
-            this.timeMicros = timeMicros;
-            this.position = position;
-        }
-
-        @Override
-        @NonNull
-        public String toString() {
-            return "[timeMicros=" + timeMicros + ", position=" + position + "]";
-        }
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null || getClass() != obj.getClass()) {
-                return false;
-            }
-            SeekPoint other = (SeekPoint) obj;
-            return timeMicros == other.timeMicros && position == other.position;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = (int) timeMicros;
-            result = 31 * result + (int) position;
-            return result;
-        }
-    }
-
-    /** Provides input data to {@link MediaParser}. */
-    public interface InputReader {
-
-        /**
-         * Reads up to {@code readLength} bytes of data and stores them into {@code buffer},
-         * starting at index {@code offset}.
-         *
-         * <p>This method blocks until at least one byte is read, the end of input is detected, or
-         * an exception is thrown. The read position advances to the first unread byte.
-         *
-         * @param buffer The buffer into which the read data should be stored.
-         * @param offset The start offset into {@code buffer} at which data should be written.
-         * @param readLength The maximum number of bytes to read.
-         * @return The non-zero number of bytes read, or -1 if no data is available because the end
-         *     of the input has been reached.
-         * @throws java.io.IOException If an error occurs reading from the source.
-         */
-        int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException;
-
-        /** Returns the current read position (byte offset) in the stream. */
-        long getPosition();
-
-        /** Returns the length of the input in bytes, or -1 if the length is unknown. */
-        long getLength();
-    }
-
-    /** {@link InputReader} that allows setting the read position. */
-    public interface SeekableInputReader extends InputReader {
-
-        /**
-         * Sets the read position at the given {@code position}.
-         *
-         * <p>{@link #advance} will immediately return after calling this method.
-         *
-         * @param position The position to seek to, in bytes.
-         */
-        void seekToPosition(long position);
-    }
-
-    /** Receives extracted media sample data and metadata from {@link MediaParser}. */
-    public interface OutputConsumer {
-
-        /**
-         * Called when a {@link SeekMap} has been extracted from the stream.
-         *
-         * <p>This method is called at least once before any samples are {@link #onSampleCompleted
-         * complete}. May be called multiple times after that in order to add {@link SeekPoint
-         * SeekPoints}.
-         *
-         * @param seekMap The extracted {@link SeekMap}.
-         */
-        void onSeekMapFound(@NonNull SeekMap seekMap);
-
-        /**
-         * Called when the number of tracks is found.
-         *
-         * @param numberOfTracks The number of tracks in the stream.
-         */
-        void onTrackCountFound(int numberOfTracks);
-
-        /**
-         * Called when new {@link TrackData} is found in the stream.
-         *
-         * @param trackIndex The index of the track for which the {@link TrackData} was extracted.
-         * @param trackData The extracted {@link TrackData}.
-         */
-        void onTrackDataFound(int trackIndex, @NonNull TrackData trackData);
-
-        /**
-         * Called when sample data is found in the stream.
-         *
-         * <p>If the invocation of this method returns before the entire {@code inputReader} {@link
-         * InputReader#getLength() length} is consumed, the method will be called again for the
-         * implementer to read the remaining data. Implementers should surface any thrown {@link
-         * IOException} caused by reading from {@code input}.
-         *
-         * @param trackIndex The index of the track to which the sample data corresponds.
-         * @param inputReader The {@link InputReader} from which to read the data.
-         * @throws IOException If an exception occurs while reading from {@code inputReader}.
-         */
-        void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException;
-
-        /**
-         * Called once all the data of a sample has been passed to {@link #onSampleDataFound}.
-         *
-         * <p>Includes sample metadata, like presentation timestamp and flags.
-         *
-         * @param trackIndex The index of the track to which the sample corresponds.
-         * @param timeMicros The media timestamp associated with the sample, in microseconds.
-         * @param flags Flags associated with the sample. See the {@code SAMPLE_FLAG_*} constants.
-         * @param size The size of the sample data, in bytes.
-         * @param offset The number of bytes that have been consumed by {@code
-         *     onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the
-         *     last byte belonging to the sample whose metadata is being passed.
-         * @param cryptoInfo Encryption data required to decrypt the sample. May be null for
-         *     unencrypted samples. Implementors should treat any output {@link CryptoInfo}
-         *     instances as immutable. MediaParser will not modify any output {@code cryptoInfos}
-         *     and implementors should not modify them either.
-         */
-        void onSampleCompleted(
-                int trackIndex,
-                long timeMicros,
-                @SampleFlags int flags,
-                int size,
-                int offset,
-                @Nullable CryptoInfo cryptoInfo);
-    }
-
-    /**
-     * Thrown if all parser implementations provided to {@link #create} failed to sniff the input
-     * content.
-     */
-    public static final class UnrecognizedInputFormatException extends IOException {
-
-        /**
-         * Creates a new instance which signals that the parsers with the given names failed to
-         * parse the input.
-         */
-        @NonNull
-        @CheckResult
-        private static UnrecognizedInputFormatException createForExtractors(
-                @NonNull String... extractorNames) {
-            StringBuilder builder = new StringBuilder();
-            builder.append("None of the available parsers ( ");
-            builder.append(extractorNames[0]);
-            for (int i = 1; i < extractorNames.length; i++) {
-                builder.append(", ");
-                builder.append(extractorNames[i]);
-            }
-            builder.append(") could read the stream.");
-            return new UnrecognizedInputFormatException(builder.toString());
-        }
-
-        private UnrecognizedInputFormatException(String extractorNames) {
-            super(extractorNames);
-        }
-    }
-
-    /** Thrown when an error occurs while parsing a media stream. */
-    public static final class ParsingException extends IOException {
-
-        private ParsingException(ParserException cause) {
-            super(cause);
-        }
-    }
-
-    // Sample flags.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(
-            flag = true,
-            value = {
-                SAMPLE_FLAG_KEY_FRAME,
-                SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA,
-                SAMPLE_FLAG_LAST_SAMPLE,
-                SAMPLE_FLAG_ENCRYPTED,
-                SAMPLE_FLAG_DECODE_ONLY
-            })
-    public @interface SampleFlags {}
-    /** Indicates that the sample holds a synchronization sample. */
-    public static final int SAMPLE_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME;
-    /**
-     * Indicates that the sample has supplemental data.
-     *
-     * <p>Samples will not have this flag set unless the {@code
-     * "android.media.mediaparser.includeSupplementalData"} parameter is set to {@code true} via
-     * {@link #setParameter}.
-     *
-     * <p>Samples with supplemental data have the following sample data format:
-     *
-     * <ul>
-     *   <li>If the {@code "android.media.mediaparser.inBandCryptoInfo"} parameter is set, all
-     *       encryption information.
-     *   <li>(4 bytes) {@code sample_data_size}: The size of the actual sample data, not including
-     *       supplemental data or encryption information.
-     *   <li>({@code sample_data_size} bytes): The media sample data.
-     *   <li>(remaining bytes) The supplemental data.
-     * </ul>
-     */
-    public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 1 << 28;
-    /** Indicates that the sample is known to contain the last media sample of the stream. */
-    public static final int SAMPLE_FLAG_LAST_SAMPLE = 1 << 29;
-    /** Indicates that the sample is (at least partially) encrypted. */
-    public static final int SAMPLE_FLAG_ENCRYPTED = 1 << 30;
-    /** Indicates that the sample should be decoded but not rendered. */
-    public static final int SAMPLE_FLAG_DECODE_ONLY = 1 << 31;
-
-    // Parser implementation names.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(
-            prefix = {"PARSER_NAME_"},
-            value = {
-                PARSER_NAME_UNKNOWN,
-                PARSER_NAME_MATROSKA,
-                PARSER_NAME_FMP4,
-                PARSER_NAME_MP4,
-                PARSER_NAME_MP3,
-                PARSER_NAME_ADTS,
-                PARSER_NAME_AC3,
-                PARSER_NAME_TS,
-                PARSER_NAME_FLV,
-                PARSER_NAME_OGG,
-                PARSER_NAME_PS,
-                PARSER_NAME_WAV,
-                PARSER_NAME_AMR,
-                PARSER_NAME_AC4,
-                PARSER_NAME_FLAC
-            })
-    public @interface ParserName {}
-
-    /** Parser name returned by {@link #getParserName()} when no parser has been selected yet. */
-    public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
-    /**
-     * Parser for the Matroska container format, as defined in the <a
-     * href="https://matroska.org/technical/specs/">spec</a>.
-     */
-    public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
-    /**
-     * Parser for fragmented files using the MP4 container format, as defined in ISO/IEC 14496-12.
-     */
-    public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
-    /**
-     * Parser for non-fragmented files using the MP4 container format, as defined in ISO/IEC
-     * 14496-12.
-     */
-    public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
-    /** Parser for the MP3 container format, as defined in ISO/IEC 11172-3. */
-    public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser";
-    /** Parser for the ADTS container format, as defined in ISO/IEC 13818-7. */
-    public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser";
-    /**
-     * Parser for the AC-3 container format, as defined in Digital Audio Compression Standard
-     * (AC-3).
-     */
-    public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser";
-    /** Parser for the TS container format, as defined in ISO/IEC 13818-1. */
-    public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
-    /**
-     * Parser for the FLV container format, as defined in Adobe Flash Video File Format
-     * Specification.
-     */
-    public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser";
-    /** Parser for the OGG container format, as defined in RFC 3533. */
-    public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
-    /** Parser for the PS container format, as defined in ISO/IEC 11172-1. */
-    public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
-    /**
-     * Parser for the WAV container format, as defined in Multimedia Programming Interface and Data
-     * Specifications.
-     */
-    public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
-    /** Parser for the AMR container format, as defined in RFC 4867. */
-    public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser";
-    /**
-     * Parser for the AC-4 container format, as defined by Dolby AC-4: Audio delivery for
-     * Next-Generation Entertainment Services.
-     */
-    public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser";
-    /**
-     * Parser for the FLAC container format, as defined in the <a
-     * href="https://xiph.org/flac/">spec</a>.
-     */
-    public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser";
-
-    // MediaParser parameters.
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef(
-            prefix = {"PARAMETER_"},
-            value = {
-                PARAMETER_ADTS_ENABLE_CBR_SEEKING,
-                PARAMETER_AMR_ENABLE_CBR_SEEKING,
-                PARAMETER_FLAC_DISABLE_ID3,
-                PARAMETER_MP4_IGNORE_EDIT_LISTS,
-                PARAMETER_MP4_IGNORE_TFDT_BOX,
-                PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES,
-                PARAMETER_MATROSKA_DISABLE_CUES_SEEKING,
-                PARAMETER_MP3_DISABLE_ID3,
-                PARAMETER_MP3_ENABLE_CBR_SEEKING,
-                PARAMETER_MP3_ENABLE_INDEX_SEEKING,
-                PARAMETER_TS_MODE,
-                PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES,
-                PARAMETER_TS_IGNORE_AAC_STREAM,
-                PARAMETER_TS_IGNORE_AVC_STREAM,
-                PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM,
-                PARAMETER_TS_DETECT_ACCESS_UNITS,
-                PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS,
-                PARAMETER_IN_BAND_CRYPTO_INFO,
-                PARAMETER_INCLUDE_SUPPLEMENTAL_DATA
-            })
-    public @interface ParameterName {}
-
-    /**
-     * Sets whether constant bitrate seeking should be enabled for ADTS parsing. {@code boolean}
-     * expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.adts.enableCbrSeeking";
-    /**
-     * Sets whether constant bitrate seeking should be enabled for AMR. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.amr.enableCbrSeeking";
-    /**
-     * Sets whether the ID3 track should be disabled for FLAC. {@code boolean} expected. Default
-     * value is {@code false}.
-     */
-    public static final String PARAMETER_FLAC_DISABLE_ID3 =
-            "android.media.mediaparser.flac.disableId3";
-    /**
-     * Sets whether MP4 parsing should ignore edit lists. {@code boolean} expected. Default value is
-     * {@code false}.
-     */
-    public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS =
-            "android.media.mediaparser.mp4.ignoreEditLists";
-    /**
-     * Sets whether MP4 parsing should ignore the tfdt box. {@code boolean} expected. Default value
-     * is {@code false}.
-     */
-    public static final String PARAMETER_MP4_IGNORE_TFDT_BOX =
-            "android.media.mediaparser.mp4.ignoreTfdtBox";
-    /**
-     * Sets whether MP4 parsing should treat all video frames as key frames. {@code boolean}
-     * expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES =
-            "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes";
-    /**
-     * Sets whether Matroska parsing should avoid seeking to the cues element. {@code boolean}
-     * expected. Default value is {@code false}.
-     *
-     * <p>If this flag is enabled and the cues element occurs after the first cluster, then the
-     * media is treated as unseekable.
-     */
-    public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING =
-            "android.media.mediaparser.matroska.disableCuesSeeking";
-    /**
-     * Sets whether the ID3 track should be disabled for MP3. {@code boolean} expected. Default
-     * value is {@code false}.
-     */
-    public static final String PARAMETER_MP3_DISABLE_ID3 =
-            "android.media.mediaparser.mp3.disableId3";
-    /**
-     * Sets whether constant bitrate seeking should be enabled for MP3. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING =
-            "android.media.mediaparser.mp3.enableCbrSeeking";
-    /**
-     * Sets whether MP3 parsing should generate a time-to-byte mapping. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek
-     * point. Therefore, it should only be used if:
-     *
-     * <ul>
-     *   <li>the file is small, or
-     *   <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata
-     *       provided in the file is not precise enough (or is not present).
-     * </ul>
-     */
-    public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING =
-            "android.media.mediaparser.mp3.enableIndexSeeking";
-    /**
-     * Sets the operation mode for TS parsing. {@code String} expected. Valid values are {@code
-     * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}.
-     *
-     * <p>The operation modes alter the way TS behaves so that it can handle certain kinds of
-     * commonly-occurring malformed media.
-     *
-     * <ul>
-     *   <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if
-     *       more PMTs are declared in the PAT.
-     *   <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1.
-     *   <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters.
-     * </ul>
-     */
-    public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode";
-    /**
-     * Sets whether TS should treat samples consisting of non-IDR I slices as synchronization
-     * samples (key-frames). {@code boolean} expected. Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES =
-            "android.media.mediaparser.ts.allowNonIdrAvcKeyframes";
-    /**
-     * Sets whether TS parsing should ignore AAC elementary streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_AAC_STREAM =
-            "android.media.mediaparser.ts.ignoreAacStream";
-    /**
-     * Sets whether TS parsing should ignore AVC elementary streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_AVC_STREAM =
-            "android.media.mediaparser.ts.ignoreAvcStream";
-    /**
-     * Sets whether TS parsing should ignore splice information streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     */
-    public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM =
-            "android.media.mediaparser.ts.ignoreSpliceInfoStream";
-    /**
-     * Sets whether TS parsing should split AVC stream into access units based on slice headers.
-     * {@code boolean} expected. Default value is {@code false}.
-     *
-     * <p>This flag should be left disabled if the stream contains access units delimiters in order
-     * to avoid unnecessary computational costs.
-     */
-    public static final String PARAMETER_TS_DETECT_ACCESS_UNITS =
-            "android.media.mediaparser.ts.ignoreDetectAccessUnits";
-    /**
-     * Sets whether TS parsing should handle HDMV DTS audio streams. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>Enabling this flag will disable the detection of SCTE subtitles.
-     */
-    public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
-            "android.media.mediaparser.ts.enableHdmvDtsAudioStreams";
-    /**
-     * Sets whether encryption data should be sent in-band with the sample data, as per {@link
-     * OutputConsumer#onSampleDataFound}. {@code boolean} expected. Default value is {@code false}.
-     *
-     * <p>If this parameter is set, encrypted samples' data will be prefixed with the encryption
-     * information bytes. The format for in-band encryption information is:
-     *
-     * <ul>
-     *   <li>(1 byte) {@code encryption_signal_byte}: Most significant bit signals whether the
-     *       encryption data contains subsample encryption data. The remaining bits contain {@code
-     *       initialization_vector_size}.
-     *   <li>({@code initialization_vector_size} bytes) Initialization vector.
-     *   <li>If subsample encryption data is present, as per {@code encryption_signal_byte}, the
-     *       encryption data also contains:
-     *       <ul>
-     *         <li>(2 bytes) {@code subsample_encryption_data_length}.
-     *         <li>({@code subsample_encryption_data_length * 6} bytes) Subsample encryption data
-     *             (repeated {@code subsample_encryption_data_length} times):
-     *             <ul>
-     *               <li>(2 bytes) Size of a clear section in sample.
-     *               <li>(4 bytes) Size of an encryption section in sample.
-     *             </ul>
-     *       </ul>
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_IN_BAND_CRYPTO_INFO =
-            "android.media.mediaparser.inBandCryptoInfo";
-    /**
-     * Sets whether supplemental data should be included as part of the sample data. {@code boolean}
-     * expected. Default value is {@code false}. See {@link #SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA} for
-     * information about the sample data format.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_INCLUDE_SUPPLEMENTAL_DATA =
-            "android.media.mediaparser.includeSupplementalData";
-    /**
-     * Sets whether sample timestamps may start from non-zero offsets. {@code boolean} expected.
-     * Default value is {@code false}.
-     *
-     * <p>When set to true, sample timestamps will not be offset to start from zero, and the media
-     * provided timestamps will be used instead. For example, transport stream sample timestamps
-     * will not be converted to a zero-based timebase.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_IGNORE_TIMESTAMP_OFFSET =
-            "android.media.mediaparser.ignoreTimestampOffset";
-    /**
-     * Sets whether each track type should be eagerly exposed. {@code boolean} expected. Default
-     * value is {@code false}.
-     *
-     * <p>When set to true, each track type will be eagerly exposed through a call to {@link
-     * OutputConsumer#onTrackDataFound} containing a single-value {@link MediaFormat}. The key for
-     * the track type is {@code "track-type-string"}, and the possible values are {@code "video"},
-     * {@code "audio"}, {@code "text"}, {@code "metadata"}, and {@code "unknown"}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EAGERLY_EXPOSE_TRACKTYPE =
-            "android.media.mediaparser.eagerlyExposeTrackType";
-    /**
-     * Sets whether a dummy {@link SeekMap} should be exposed before starting extraction. {@code
-     * boolean} expected. Default value is {@code false}.
-     *
-     * <p>For each {@link SeekMap#getSeekPoints} call, the dummy {@link SeekMap} returns a single
-     * {@link SeekPoint} whose {@link SeekPoint#timeMicros} matches the requested timestamp, and
-     * whose {@link SeekPoint#position} is 0.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_DUMMY_SEEKMAP =
-            "android.media.mediaparser.exposeDummySeekMap";
-
-    /**
-     * Sets whether chunk indices available in the extracted media should be exposed as {@link
-     * MediaFormat MediaFormats}. {@code boolean} expected. Default value is {@link false}.
-     *
-     * <p>When set to true, any information about media segmentation will be exposed as a {@link
-     * MediaFormat} (with track index 0) containing four {@link ByteBuffer} elements under the
-     * following keys:
-     *
-     * <ul>
-     *   <li>"chunk-index-int-sizes": Contains {@code ints} representing the sizes in bytes of each
-     *       of the media segments.
-     *   <li>"chunk-index-long-offsets": Contains {@code longs} representing the byte offsets of
-     *       each segment in the stream.
-     *   <li>"chunk-index-long-us-durations": Contains {@code longs} representing the media duration
-     *       of each segment, in microseconds.
-     *   <li>"chunk-index-long-us-times": Contains {@code longs} representing the start time of each
-     *       segment, in microseconds.
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT =
-            "android.media.mediaParser.exposeChunkIndexAsMediaFormat";
-    /**
-     * Sets a list of closed-caption {@link MediaFormat MediaFormats} that should be exposed as part
-     * of the extracted media. {@code List<MediaFormat>} expected. Default value is an empty list.
-     *
-     * <p>Expected keys in the {@link MediaFormat} are:
-     *
-     * <ul>
-     *   <p>{@link MediaFormat#KEY_MIME}: Determine the type of captions (for example,
-     *   application/cea-608). Mandatory.
-     *   <p>{@link MediaFormat#KEY_CAPTION_SERVICE_NUMBER}: Determine the channel on which the
-     *   captions are transmitted. Optional.
-     * </ul>
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_CAPTION_FORMATS =
-            "android.media.mediaParser.exposeCaptionFormats";
-    /**
-     * Sets whether the value associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS} should
-     * override any in-band caption service declarations. {@code boolean} expected. Default value is
-     * {@link false}.
-     *
-     * <p>When {@code false}, any present in-band caption services information will override the
-     * values associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS =
-            "android.media.mediaParser.overrideInBandCaptionDeclarations";
-    /**
-     * Sets whether a track for EMSG events should be exposed in case of parsing a container that
-     * supports them. {@code boolean} expected. Default value is {@link false}.
-     *
-     * @hide
-     */
-    public static final String PARAMETER_EXPOSE_EMSG_TRACK =
-            "android.media.mediaParser.exposeEmsgTrack";
-
-    // Private constants.
-
-    private static final String TAG = "MediaParser";
-    private static final String JNI_LIBRARY_NAME = "mediaparser-jni";
-    private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
-    private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME;
-    private static final String TS_MODE_SINGLE_PMT = "single_pmt";
-    private static final String TS_MODE_MULTI_PMT = "multi_pmt";
-    private static final String TS_MODE_HLS = "hls";
-    private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
-    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
-    private static final String MEDIAMETRICS_ELEMENT_SEPARATOR = "|";
-    private static final int MEDIAMETRICS_MAX_STRING_SIZE = 200;
-    private static final int MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH;
-    /**
-     * Intentional error introduced to reported metrics to prevent identification of the parsed
-     * media. Note: Increasing this value may cause older hostside CTS tests to fail.
-     */
-    private static final float MEDIAMETRICS_DITHER = .02f;
-
-    @IntDef(
-            value = {
-                STATE_READING_SIGNAL_BYTE,
-                STATE_READING_INIT_VECTOR,
-                STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE,
-                STATE_READING_SUBSAMPLE_ENCRYPTION_DATA
-            })
-    private @interface EncryptionDataReadState {}
-
-    private static final int STATE_READING_SIGNAL_BYTE = 0;
-    private static final int STATE_READING_INIT_VECTOR = 1;
-    private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE = 2;
-    private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_DATA = 3;
-
-    // Instance creation methods.
-
-    /**
-     * Creates an instance backed by the parser with the given {@code name}. The returned instance
-     * will attempt parsing without sniffing the content.
-     *
-     * @param name The name of the parser that will be associated with the created instance.
-     * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed.
-     * @return A new instance.
-     * @throws IllegalArgumentException If an invalid name is provided.
-     */
-    @NonNull
-    public static MediaParser createByName(
-            @NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) {
-        String[] nameAsArray = new String[] {name};
-        assertValidNames(nameAsArray);
-        return new MediaParser(outputConsumer, /* createdByName= */ true, name);
-    }
-
-    /**
-     * Creates an instance whose backing parser will be selected by sniffing the content during the
-     * first {@link #advance} call. Parser implementations will sniff the content in order of
-     * appearance in {@code parserNames}.
-     *
-     * @param outputConsumer The {@link OutputConsumer} to which extracted data is output.
-     * @param parserNames The names of the parsers to sniff the content with. If empty, a default
-     *     array of names is used.
-     * @return A new instance.
-     */
-    @NonNull
-    public static MediaParser create(
-            @NonNull OutputConsumer outputConsumer, @NonNull @ParserName String... parserNames) {
-        assertValidNames(parserNames);
-        if (parserNames.length == 0) {
-            parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
-        }
-        return new MediaParser(outputConsumer, /* createdByName= */ false, parserNames);
-    }
-
-    // Misc static methods.
-
-    /**
-     * Returns an immutable list with the names of the parsers that are suitable for container
-     * formats with the given {@link MediaFormat}.
-     *
-     * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
-     * MediaFormat#KEY_MIME} corresponds to the supported container format.
-     *
-     * @param mediaFormat The {@link MediaFormat} to check support for.
-     * @return The parser names that support the given {@code mediaFormat}, or the list of all
-     *     parsers available if no container specific format information is provided.
-     */
-    @NonNull
-    @ParserName
-    public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
-        String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
-        mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
-        if (TextUtils.isEmpty(mimeType)) {
-            // No MIME type provided. Return all.
-            return Collections.unmodifiableList(
-                    new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
-        }
-        ArrayList<String> result = new ArrayList<>();
-        switch (mimeType) {
-            case "video/x-matroska":
-            case "audio/x-matroska":
-            case "video/x-webm":
-            case "audio/x-webm":
-                result.add(PARSER_NAME_MATROSKA);
-                break;
-            case "video/mp4":
-            case "audio/mp4":
-            case "application/mp4":
-                result.add(PARSER_NAME_MP4);
-                result.add(PARSER_NAME_FMP4);
-                break;
-            case "audio/mpeg":
-                result.add(PARSER_NAME_MP3);
-                break;
-            case "audio/aac":
-                result.add(PARSER_NAME_ADTS);
-                break;
-            case "audio/ac3":
-                result.add(PARSER_NAME_AC3);
-                break;
-            case "video/mp2t":
-            case "audio/mp2t":
-                result.add(PARSER_NAME_TS);
-                break;
-            case "video/x-flv":
-                result.add(PARSER_NAME_FLV);
-                break;
-            case "video/ogg":
-            case "audio/ogg":
-            case "application/ogg":
-                result.add(PARSER_NAME_OGG);
-                break;
-            case "video/mp2p":
-            case "video/mp1s":
-                result.add(PARSER_NAME_PS);
-                break;
-            case "audio/vnd.wave":
-            case "audio/wav":
-            case "audio/wave":
-            case "audio/x-wav":
-                result.add(PARSER_NAME_WAV);
-                break;
-            case "audio/amr":
-                result.add(PARSER_NAME_AMR);
-                break;
-            case "audio/ac4":
-                result.add(PARSER_NAME_AC4);
-                break;
-            case "audio/flac":
-            case "audio/x-flac":
-                result.add(PARSER_NAME_FLAC);
-                break;
-            default:
-                // No parsers support the given mime type. Do nothing.
-                break;
-        }
-        return Collections.unmodifiableList(result);
-    }
-
-    // Private fields.
-
-    private final Map<String, Object> mParserParameters;
-    private final OutputConsumer mOutputConsumer;
-    private final String[] mParserNamesPool;
-    private final PositionHolder mPositionHolder;
-    private final InputReadingDataReader mExoDataReader;
-    private final DataReaderAdapter mScratchDataReaderAdapter;
-    private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
-    @Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor;
-    private final ArrayList<Format> mMuxedCaptionFormats;
-    private boolean mInBandCryptoInfo;
-    private boolean mIncludeSupplementalData;
-    private boolean mIgnoreTimestampOffset;
-    private boolean mEagerlyExposeTrackType;
-    private boolean mExposeDummySeekMap;
-    private boolean mExposeChunkIndexAsMediaFormat;
-    private String mParserName;
-    private Extractor mExtractor;
-    private ExtractorInput mExtractorInput;
-    private boolean mPendingExtractorInit;
-    private long mPendingSeekPosition;
-    private long mPendingSeekTimeMicros;
-    private boolean mLoggedSchemeInitDataCreationException;
-    private boolean mReleased;
-
-    // MediaMetrics fields.
-    @Nullable private LogSessionId mLogSessionId;
-    private final boolean mCreatedByName;
-    private final SparseArray<Format> mTrackFormats;
-    private String mLastObservedExceptionName;
-    private long mDurationMillis;
-    private long mResourceByteCount;
-
-    // Public methods.
-
-    /**
-     * Sets parser-specific parameters which allow customizing behavior.
-     *
-     * <p>Must be called before the first call to {@link #advance}.
-     *
-     * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for
-     *     documentation on possible values.
-     * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*}
-     *     constants for documentation on the expected types.
-     * @return This instance, for convenience.
-     * @throws IllegalStateException If called after calling {@link #advance} on the same instance.
-     */
-    @NonNull
-    public MediaParser setParameter(
-            @NonNull @ParameterName String parameterName, @NonNull Object value) {
-        if (mExtractor != null) {
-            throw new IllegalStateException(
-                    "setParameters() must be called before the first advance() call.");
-        }
-        Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName);
-        // Ignore parameter names that are not contained in the map, in case the client is passing
-        // a parameter that is being added in a future version of this library.
-        if (expectedType != null && !expectedType.isInstance(value)) {
-            throw new IllegalArgumentException(
-                    parameterName
-                            + " expects a "
-                            + expectedType.getSimpleName()
-                            + " but a "
-                            + value.getClass().getSimpleName()
-                            + " was passed.");
-        }
-        if (PARAMETER_TS_MODE.equals(parameterName)
-                && !TS_MODE_SINGLE_PMT.equals(value)
-                && !TS_MODE_HLS.equals(value)
-                && !TS_MODE_MULTI_PMT.equals(value)) {
-            throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value);
-        }
-        if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) {
-            mInBandCryptoInfo = (boolean) value;
-        }
-        if (PARAMETER_INCLUDE_SUPPLEMENTAL_DATA.equals(parameterName)) {
-            mIncludeSupplementalData = (boolean) value;
-        }
-        if (PARAMETER_IGNORE_TIMESTAMP_OFFSET.equals(parameterName)) {
-            mIgnoreTimestampOffset = (boolean) value;
-        }
-        if (PARAMETER_EAGERLY_EXPOSE_TRACKTYPE.equals(parameterName)) {
-            mEagerlyExposeTrackType = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_DUMMY_SEEKMAP.equals(parameterName)) {
-            mExposeDummySeekMap = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT.equals(parameterName)) {
-            mExposeChunkIndexAsMediaFormat = (boolean) value;
-        }
-        if (PARAMETER_EXPOSE_CAPTION_FORMATS.equals(parameterName)) {
-            setMuxedCaptionFormats((List<MediaFormat>) value);
-        }
-        mParserParameters.put(parameterName, value);
-        return this;
-    }
-
-    /**
-     * Returns whether the given {@code parameterName} is supported by this parser.
-     *
-     * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*}
-     *     constants.
-     * @return Whether the given {@code parameterName} is supported.
-     */
-    public boolean supportsParameter(@NonNull @ParameterName String parameterName) {
-        return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName);
-    }
-
-    /**
-     * Returns the name of the backing parser implementation.
-     *
-     * <p>If this instance was creating using {@link #createByName}, the provided name is returned.
-     * If this instance was created using {@link #create}, this method will return {@link
-     * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the
-     * backing parser implementation is returned.
-     *
-     * @return The name of the backing parser implementation, or null if the backing parser
-     *     implementation has not yet been selected.
-     */
-    @NonNull
-    @ParserName
-    public String getParserName() {
-        return mParserName;
-    }
-
-    /**
-     * Makes progress in the extraction of the input media stream, unless the end of the input has
-     * been reached.
-     *
-     * <p>This method will block until some progress has been made.
-     *
-     * <p>If this instance was created using {@link #create}, the first call to this method will
-     * sniff the content using the selected parser implementations.
-     *
-     * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
-     *     container data.
-     * @return Whether there is any data left to extract. Returns false if the end of input has been
-     *     reached.
-     * @throws IOException If an error occurs while reading from the {@link SeekableInputReader}.
-     * @throws UnrecognizedInputFormatException If the format cannot be recognized by any of the
-     *     underlying parser implementations.
-     */
-    public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException {
-        if (mExtractorInput == null) {
-            // TODO: For efficiency, the same implementation should be used, by providing a
-            // clearBuffers() method, or similar.
-            long resourceLength = seekableInputReader.getLength();
-            if (mResourceByteCount == 0) {
-                // For resource byte count metric collection, we only take into account the length
-                // of the first provided input reader.
-                mResourceByteCount = resourceLength;
-            }
-            mExtractorInput =
-                    new DefaultExtractorInput(
-                            mExoDataReader, seekableInputReader.getPosition(), resourceLength);
-        }
-        mExoDataReader.mInputReader = seekableInputReader;
-
-        if (mExtractor == null) {
-            mPendingExtractorInit = true;
-            if (!mParserName.equals(PARSER_NAME_UNKNOWN)) {
-                mExtractor = createExtractor(mParserName);
-            } else {
-                for (String parserName : mParserNamesPool) {
-                    Extractor extractor = createExtractor(parserName);
-                    try {
-                        if (extractor.sniff(mExtractorInput)) {
-                            mParserName = parserName;
-                            mExtractor = extractor;
-                            mPendingExtractorInit = true;
-                            break;
-                        }
-                    } catch (EOFException e) {
-                        // Do nothing.
-                    } finally {
-                        mExtractorInput.resetPeekPosition();
-                    }
-                }
-                if (mExtractor == null) {
-                    UnrecognizedInputFormatException exception =
-                            UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
-                    mLastObservedExceptionName = exception.getClass().getName();
-                    throw exception;
-                }
-                return true;
-            }
-        }
-
-        if (mPendingExtractorInit) {
-            if (mExposeDummySeekMap) {
-                // We propagate the dummy seek map before initializing the extractor, in case the
-                // extractor initialization outputs a seek map.
-                mOutputConsumer.onSeekMapFound(SeekMap.DUMMY);
-            }
-            mExtractor.init(new ExtractorOutputAdapter());
-            mPendingExtractorInit = false;
-            // We return after initialization to allow clients use any output information before
-            // starting actual extraction.
-            return true;
-        }
-
-        if (isPendingSeek()) {
-            mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros);
-            removePendingSeek();
-        }
-
-        mPositionHolder.position = seekableInputReader.getPosition();
-        int result;
-        try {
-            result = mExtractor.read(mExtractorInput, mPositionHolder);
-        } catch (Exception e) {
-            mLastObservedExceptionName = e.getClass().getName();
-            if (e instanceof ParserException) {
-                throw new ParsingException((ParserException) e);
-            } else {
-                throw e;
-            }
-        }
-        if (result == Extractor.RESULT_END_OF_INPUT) {
-            mExtractorInput = null;
-            return false;
-        }
-        if (result == Extractor.RESULT_SEEK) {
-            mExtractorInput = null;
-            seekableInputReader.seekToPosition(mPositionHolder.position);
-        }
-        return true;
-    }
-
-    /**
-     * Seeks within the media container being extracted.
-     *
-     * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
-     * OutputConsumer#onSeekMapFound(SeekMap)}.
-     *
-     * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
-     * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
-     *
-     * @param seekPoint The {@link SeekPoint} to seek to.
-     */
-    public void seek(@NonNull SeekPoint seekPoint) {
-        if (mExtractor == null) {
-            mPendingSeekPosition = seekPoint.position;
-            mPendingSeekTimeMicros = seekPoint.timeMicros;
-        } else {
-            mExtractor.seek(seekPoint.position, seekPoint.timeMicros);
-        }
-    }
-
-    /**
-     * Releases any acquired resources.
-     *
-     * <p>After calling this method, this instance becomes unusable and no other methods should be
-     * invoked.
-     */
-    public void release() {
-        mExtractorInput = null;
-        mExtractor = null;
-        if (mReleased) {
-            // Nothing to do.
-            return;
-        }
-        mReleased = true;
-
-        String trackMimeTypes = buildMediaMetricsString(format -> format.sampleMimeType);
-        String trackCodecs = buildMediaMetricsString(format -> format.codecs);
-        int videoWidth = -1;
-        int videoHeight = -1;
-        for (int i = 0; i < mTrackFormats.size(); i++) {
-            Format format = mTrackFormats.valueAt(i);
-            if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) {
-                videoWidth = format.width;
-                videoHeight = format.height;
-                break;
-            }
-        }
-
-        String alteredParameters =
-                String.join(
-                        MEDIAMETRICS_ELEMENT_SEPARATOR,
-                        mParserParameters.keySet().toArray(new String[0]));
-        alteredParameters =
-                alteredParameters.substring(
-                        0,
-                        Math.min(
-                                alteredParameters.length(),
-                                MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH));
-
-        nativeSubmitMetrics(
-                SdkLevel.isAtLeastS() ? getLogSessionIdStringV31() : "",
-                mParserName,
-                mCreatedByName,
-                String.join(MEDIAMETRICS_ELEMENT_SEPARATOR, mParserNamesPool),
-                mLastObservedExceptionName,
-                addDither(mResourceByteCount),
-                addDither(mDurationMillis),
-                trackMimeTypes,
-                trackCodecs,
-                alteredParameters,
-                videoWidth,
-                videoHeight);
-    }
-
-    @RequiresApi(31)
-    public void setLogSessionId(@NonNull LogSessionId logSessionId) {
-        this.mLogSessionId = Objects.requireNonNull(logSessionId);
-    }
-
-    @RequiresApi(31)
-    @NonNull
-    public LogSessionId getLogSessionId() {
-        return mLogSessionId != null ? mLogSessionId : LogSessionId.LOG_SESSION_ID_NONE;
-    }
-
-    // Private methods.
-
-    private MediaParser(
-            OutputConsumer outputConsumer, boolean createdByName, String... parserNamesPool) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            throw new UnsupportedOperationException("Android version must be R or greater.");
-        }
-        mParserParameters = new HashMap<>();
-        mOutputConsumer = outputConsumer;
-        mParserNamesPool = parserNamesPool;
-        mCreatedByName = createdByName;
-        mParserName = createdByName ? parserNamesPool[0] : PARSER_NAME_UNKNOWN;
-        mPositionHolder = new PositionHolder();
-        mExoDataReader = new InputReadingDataReader();
-        removePendingSeek();
-        mScratchDataReaderAdapter = new DataReaderAdapter();
-        mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
-        mSchemeInitDataConstructor = getSchemeInitDataConstructor();
-        mMuxedCaptionFormats = new ArrayList<>();
-
-        // MediaMetrics.
-        mTrackFormats = new SparseArray<>();
-        mLastObservedExceptionName = "";
-        mDurationMillis = -1;
-    }
-
-    private String buildMediaMetricsString(Function<Format, String> formatFieldGetter) {
-        StringBuilder stringBuilder = new StringBuilder();
-        for (int i = 0; i < mTrackFormats.size(); i++) {
-            if (i > 0) {
-                stringBuilder.append(MEDIAMETRICS_ELEMENT_SEPARATOR);
-            }
-            String fieldValue = formatFieldGetter.apply(mTrackFormats.valueAt(i));
-            stringBuilder.append(fieldValue != null ? fieldValue : "");
-        }
-        return stringBuilder.substring(
-                0, Math.min(stringBuilder.length(), MEDIAMETRICS_MAX_STRING_SIZE));
-    }
-
-    private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) {
-        mMuxedCaptionFormats.clear();
-        for (MediaFormat mediaFormat : mediaFormats) {
-            mMuxedCaptionFormats.add(toExoPlayerCaptionFormat(mediaFormat));
-        }
-    }
-
-    private boolean isPendingSeek() {
-        return mPendingSeekPosition >= 0;
-    }
-
-    private void removePendingSeek() {
-        mPendingSeekPosition = -1;
-        mPendingSeekTimeMicros = -1;
-    }
-
-    private Extractor createExtractor(String parserName) {
-        int flags = 0;
-        TimestampAdjuster timestampAdjuster = null;
-        if (mIgnoreTimestampOffset) {
-            timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET);
-        }
-        switch (parserName) {
-            case PARSER_NAME_MATROSKA:
-                flags =
-                        getBooleanParameter(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING)
-                                ? MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES
-                                : 0;
-                return new MatroskaExtractor(flags);
-            case PARSER_NAME_FMP4:
-                flags |=
-                        getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK)
-                                ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
-                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_TFDT_BOX)
-                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES)
-                                ? FragmentedMp4Extractor
-                                        .FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
-                                : 0;
-                return new FragmentedMp4Extractor(
-                        flags,
-                        timestampAdjuster,
-                        /* sideloadedTrack= */ null,
-                        mMuxedCaptionFormats);
-            case PARSER_NAME_MP4:
-                flags |=
-                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
-                                ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
-                                : 0;
-                return new Mp4Extractor(flags);
-            case PARSER_NAME_MP3:
-                flags |=
-                        getBooleanParameter(PARAMETER_MP3_DISABLE_ID3)
-                                ? Mp3Extractor.FLAG_DISABLE_ID3_METADATA
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_MP3_ENABLE_CBR_SEEKING)
-                                ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                // TODO: Add index seeking once we update the ExoPlayer version.
-                return new Mp3Extractor(flags);
-            case PARSER_NAME_ADTS:
-                flags |=
-                        getBooleanParameter(PARAMETER_ADTS_ENABLE_CBR_SEEKING)
-                                ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                return new AdtsExtractor(flags);
-            case PARSER_NAME_AC3:
-                return new Ac3Extractor();
-            case PARSER_NAME_TS:
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES)
-                                ? DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_DETECT_ACCESS_UNITS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_AAC_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_AVC_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM)
-                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM
-                                : 0;
-                flags |=
-                        getBooleanParameter(PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS)
-                                ? DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS
-                                : 0;
-                String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT);
-                int hlsMode =
-                        TS_MODE_SINGLE_PMT.equals(tsMode)
-                                ? TsExtractor.MODE_SINGLE_PMT
-                                : TS_MODE_HLS.equals(tsMode)
-                                        ? TsExtractor.MODE_HLS
-                                        : TsExtractor.MODE_MULTI_PMT;
-                return new TsExtractor(
-                        hlsMode,
-                        timestampAdjuster != null
-                                ? timestampAdjuster
-                                : new TimestampAdjuster(/* firstSampleTimestampUs= */ 0),
-                        new DefaultTsPayloadReaderFactory(flags, mMuxedCaptionFormats));
-            case PARSER_NAME_FLV:
-                return new FlvExtractor();
-            case PARSER_NAME_OGG:
-                return new OggExtractor();
-            case PARSER_NAME_PS:
-                return new PsExtractor();
-            case PARSER_NAME_WAV:
-                return new WavExtractor();
-            case PARSER_NAME_AMR:
-                flags |=
-                        getBooleanParameter(PARAMETER_AMR_ENABLE_CBR_SEEKING)
-                                ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
-                                : 0;
-                return new AmrExtractor(flags);
-            case PARSER_NAME_AC4:
-                return new Ac4Extractor();
-            case PARSER_NAME_FLAC:
-                flags |=
-                        getBooleanParameter(PARAMETER_FLAC_DISABLE_ID3)
-                                ? FlacExtractor.FLAG_DISABLE_ID3_METADATA
-                                : 0;
-                return new FlacExtractor(flags);
-            default:
-                // Should never happen.
-                throw new IllegalStateException("Unexpected attempt to create: " + parserName);
-        }
-    }
-
-    private boolean getBooleanParameter(String name) {
-        return (boolean) mParserParameters.getOrDefault(name, false);
-    }
-
-    private String getStringParameter(String name, String defaultValue) {
-        return (String) mParserParameters.getOrDefault(name, defaultValue);
-    }
-
-    @RequiresApi(31)
-    private String getLogSessionIdStringV31() {
-        return mLogSessionId != null ? mLogSessionId.getStringId() : "";
-    }
-
-    // Private classes.
-
-    private static final class InputReadingDataReader implements DataReader {
-
-        public InputReader mInputReader;
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) throws IOException {
-            return mInputReader.read(buffer, offset, readLength);
-        }
-    }
-
-    private final class MediaParserDrmInitData extends DrmInitData {
-
-        private final SchemeInitData[] mSchemeDatas;
-
-        private MediaParserDrmInitData(com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData)
-                throws IllegalAccessException, InstantiationException, InvocationTargetException {
-            mSchemeDatas = new SchemeInitData[exoDrmInitData.schemeDataCount];
-            for (int i = 0; i < mSchemeDatas.length; i++) {
-                mSchemeDatas[i] = toFrameworkSchemeInitData(exoDrmInitData.get(i));
-            }
-        }
-
-        @Override
-        @Nullable
-        public SchemeInitData get(UUID schemeUuid) {
-            for (SchemeInitData schemeInitData : mSchemeDatas) {
-                if (schemeInitData.uuid.equals(schemeUuid)) {
-                    return schemeInitData;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public SchemeInitData getSchemeInitDataAt(int index) {
-            return mSchemeDatas[index];
-        }
-
-        @Override
-        public int getSchemeInitDataCount() {
-            return mSchemeDatas.length;
-        }
-
-        private DrmInitData.SchemeInitData toFrameworkSchemeInitData(SchemeData exoSchemeData)
-                throws IllegalAccessException, InvocationTargetException, InstantiationException {
-            return mSchemeInitDataConstructor.newInstance(
-                    exoSchemeData.uuid, exoSchemeData.mimeType, exoSchemeData.data);
-        }
-    }
-
-    private final class ExtractorOutputAdapter implements ExtractorOutput {
-
-        private final SparseArray<TrackOutput> mTrackOutputAdapters;
-        private boolean mTracksEnded;
-
-        private ExtractorOutputAdapter() {
-            mTrackOutputAdapters = new SparseArray<>();
-        }
-
-        @Override
-        public TrackOutput track(int id, int type) {
-            TrackOutput trackOutput = mTrackOutputAdapters.get(id);
-            if (trackOutput == null) {
-                int trackIndex = mTrackOutputAdapters.size();
-                trackOutput = new TrackOutputAdapter(trackIndex);
-                mTrackOutputAdapters.put(id, trackOutput);
-                if (mEagerlyExposeTrackType) {
-                    MediaFormat mediaFormat = new MediaFormat();
-                    mediaFormat.setString("track-type-string", toTypeString(type));
-                    mOutputConsumer.onTrackDataFound(
-                            trackIndex, new TrackData(mediaFormat, /* drmInitData= */ null));
-                }
-            }
-            return trackOutput;
-        }
-
-        @Override
-        public void endTracks() {
-            mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size());
-        }
-
-        @Override
-        public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            long durationUs = exoplayerSeekMap.getDurationUs();
-            if (durationUs != C.TIME_UNSET) {
-                mDurationMillis = C.usToMs(durationUs);
-            }
-            if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) {
-                ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap;
-                MediaFormat mediaFormat = new MediaFormat();
-                mediaFormat.setByteBuffer("chunk-index-int-sizes", toByteBuffer(chunkIndex.sizes));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-offsets", toByteBuffer(chunkIndex.offsets));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-us-durations", toByteBuffer(chunkIndex.durationsUs));
-                mediaFormat.setByteBuffer(
-                        "chunk-index-long-us-times", toByteBuffer(chunkIndex.timesUs));
-                mOutputConsumer.onTrackDataFound(
-                        /* trackIndex= */ 0, new TrackData(mediaFormat, /* drmInitData= */ null));
-            }
-            mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap));
-        }
-    }
-
-    private class TrackOutputAdapter implements TrackOutput {
-
-        private final int mTrackIndex;
-
-        private CryptoInfo mLastOutputCryptoInfo;
-        private CryptoInfo.Pattern mLastOutputEncryptionPattern;
-        private CryptoData mLastReceivedCryptoData;
-
-        @EncryptionDataReadState private int mEncryptionDataReadState;
-        private int mEncryptionDataSizeToSubtractFromSampleDataSize;
-        private int mEncryptionVectorSize;
-        private byte[] mScratchIvSpace;
-        private int mSubsampleEncryptionDataSize;
-        private int[] mScratchSubsampleEncryptedBytesCount;
-        private int[] mScratchSubsampleClearBytesCount;
-        private boolean mHasSubsampleEncryptionData;
-        private int mSkippedSupplementalDataBytes;
-
-        private TrackOutputAdapter(int trackIndex) {
-            mTrackIndex = trackIndex;
-            mScratchIvSpace = new byte[16]; // Size documented in CryptoInfo.
-            mScratchSubsampleEncryptedBytesCount = new int[32];
-            mScratchSubsampleClearBytesCount = new int[32];
-            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-            mLastOutputEncryptionPattern =
-                    new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0);
-        }
-
-        @Override
-        public void format(Format format) {
-            mTrackFormats.put(mTrackIndex, format);
-            mOutputConsumer.onTrackDataFound(
-                    mTrackIndex,
-                    new TrackData(
-                            toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData)));
-        }
-
-        @Override
-        public int sampleData(
-                DataReader input,
-                int length,
-                boolean allowEndOfInput,
-                @SampleDataPart int sampleDataPart)
-                throws IOException {
-            mScratchDataReaderAdapter.setDataReader(input, length);
-            long positionBeforeReading = mScratchDataReaderAdapter.getPosition();
-            mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchDataReaderAdapter);
-            return (int) (mScratchDataReaderAdapter.getPosition() - positionBeforeReading);
-        }
-
-        @Override
-        public void sampleData(
-                ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) {
-            if (sampleDataPart == SAMPLE_DATA_PART_ENCRYPTION && !mInBandCryptoInfo) {
-                while (length > 0) {
-                    switch (mEncryptionDataReadState) {
-                        case STATE_READING_SIGNAL_BYTE:
-                            int encryptionSignalByte = data.readUnsignedByte();
-                            length--;
-                            mHasSubsampleEncryptionData = ((encryptionSignalByte >> 7) & 1) != 0;
-                            mEncryptionVectorSize = encryptionSignalByte & 0x7F;
-                            mEncryptionDataSizeToSubtractFromSampleDataSize =
-                                    mEncryptionVectorSize + 1; // Signal byte.
-                            mEncryptionDataReadState = STATE_READING_INIT_VECTOR;
-                            break;
-                        case STATE_READING_INIT_VECTOR:
-                            Arrays.fill(mScratchIvSpace, (byte) 0); // Ensure 0-padding.
-                            data.readBytes(mScratchIvSpace, /* offset= */ 0, mEncryptionVectorSize);
-                            length -= mEncryptionVectorSize;
-                            if (mHasSubsampleEncryptionData) {
-                                mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE;
-                            } else {
-                                mSubsampleEncryptionDataSize = 0;
-                                mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-                            }
-                            break;
-                        case STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE:
-                            mSubsampleEncryptionDataSize = data.readUnsignedShort();
-                            if (mScratchSubsampleClearBytesCount.length
-                                    < mSubsampleEncryptionDataSize) {
-                                mScratchSubsampleClearBytesCount =
-                                        new int[mSubsampleEncryptionDataSize];
-                                mScratchSubsampleEncryptedBytesCount =
-                                        new int[mSubsampleEncryptionDataSize];
-                            }
-                            length -= 2;
-                            mEncryptionDataSizeToSubtractFromSampleDataSize +=
-                                    2
-                                            + mSubsampleEncryptionDataSize
-                                                    * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
-                            mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_DATA;
-                            break;
-                        case STATE_READING_SUBSAMPLE_ENCRYPTION_DATA:
-                            for (int i = 0; i < mSubsampleEncryptionDataSize; i++) {
-                                mScratchSubsampleClearBytesCount[i] = data.readUnsignedShort();
-                                mScratchSubsampleEncryptedBytesCount[i] = data.readInt();
-                            }
-                            length -=
-                                    mSubsampleEncryptionDataSize
-                                            * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY;
-                            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-                            if (length != 0) {
-                                throw new IllegalStateException();
-                            }
-                            break;
-                        default:
-                            // Never happens.
-                            throw new IllegalStateException();
-                    }
-                }
-            } else if (sampleDataPart == SAMPLE_DATA_PART_SUPPLEMENTAL
-                    && !mIncludeSupplementalData) {
-                mSkippedSupplementalDataBytes += length;
-                data.skipBytes(length);
-            } else {
-                outputSampleData(data, length);
-            }
-        }
-
-        @Override
-        public void sampleMetadata(
-                long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) {
-            size -= mSkippedSupplementalDataBytes;
-            mSkippedSupplementalDataBytes = 0;
-            mOutputConsumer.onSampleCompleted(
-                    mTrackIndex,
-                    timeUs,
-                    getMediaParserFlags(flags),
-                    size - mEncryptionDataSizeToSubtractFromSampleDataSize,
-                    offset,
-                    getPopulatedCryptoInfo(cryptoData));
-            mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE;
-            mEncryptionDataSizeToSubtractFromSampleDataSize = 0;
-        }
-
-        @Nullable
-        private CryptoInfo getPopulatedCryptoInfo(@Nullable CryptoData cryptoData) {
-            if (cryptoData == null) {
-                // The sample is not encrypted.
-                return null;
-            } else if (mInBandCryptoInfo) {
-                if (cryptoData != mLastReceivedCryptoData) {
-                    mLastOutputCryptoInfo =
-                            createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
-                    // We are using in-band crypto info, so the IV will be ignored. But we prevent
-                    // it from being null because toString assumes it non-null.
-                    mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY;
-                }
-            } else /* We must populate the full CryptoInfo. */ {
-                // CryptoInfo.pattern is not accessible to the user, so the user needs to feed
-                // this CryptoInfo directly to MediaCodec. We need to create a new CryptoInfo per
-                // sample because of per-sample initialization vector changes.
-                CryptoInfo newCryptoInfo = createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
-                newCryptoInfo.iv = Arrays.copyOf(mScratchIvSpace, mScratchIvSpace.length);
-                boolean canReuseSubsampleInfo =
-                        mLastOutputCryptoInfo != null
-                                && mLastOutputCryptoInfo.numSubSamples
-                                        == mSubsampleEncryptionDataSize;
-                for (int i = 0; i < mSubsampleEncryptionDataSize && canReuseSubsampleInfo; i++) {
-                    canReuseSubsampleInfo =
-                            mLastOutputCryptoInfo.numBytesOfClearData[i]
-                                            == mScratchSubsampleClearBytesCount[i]
-                                    && mLastOutputCryptoInfo.numBytesOfEncryptedData[i]
-                                            == mScratchSubsampleEncryptedBytesCount[i];
-                }
-                newCryptoInfo.numSubSamples = mSubsampleEncryptionDataSize;
-                if (canReuseSubsampleInfo) {
-                    newCryptoInfo.numBytesOfClearData = mLastOutputCryptoInfo.numBytesOfClearData;
-                    newCryptoInfo.numBytesOfEncryptedData =
-                            mLastOutputCryptoInfo.numBytesOfEncryptedData;
-                } else {
-                    newCryptoInfo.numBytesOfClearData =
-                            Arrays.copyOf(
-                                    mScratchSubsampleClearBytesCount, mSubsampleEncryptionDataSize);
-                    newCryptoInfo.numBytesOfEncryptedData =
-                            Arrays.copyOf(
-                                    mScratchSubsampleEncryptedBytesCount,
-                                    mSubsampleEncryptionDataSize);
-                }
-                mLastOutputCryptoInfo = newCryptoInfo;
-            }
-            mLastReceivedCryptoData = cryptoData;
-            return mLastOutputCryptoInfo;
-        }
-
-        private CryptoInfo createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData) {
-            CryptoInfo cryptoInfo = new CryptoInfo();
-            cryptoInfo.key = cryptoData.encryptionKey;
-            cryptoInfo.mode = cryptoData.cryptoMode;
-            if (cryptoData.clearBlocks != mLastOutputEncryptionPattern.getSkipBlocks()
-                    || cryptoData.encryptedBlocks
-                            != mLastOutputEncryptionPattern.getEncryptBlocks()) {
-                mLastOutputEncryptionPattern =
-                        new CryptoInfo.Pattern(cryptoData.encryptedBlocks, cryptoData.clearBlocks);
-            }
-            cryptoInfo.setPattern(mLastOutputEncryptionPattern);
-            return cryptoInfo;
-        }
-
-        private void outputSampleData(ParsableByteArray data, int length) {
-            mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
-            try {
-                // Read all bytes from data. ExoPlayer extractors expect all sample data to be
-                // consumed by TrackOutput implementations when passing a ParsableByteArray.
-                while (mScratchParsableByteArrayAdapter.getLength() > 0) {
-                    mOutputConsumer.onSampleDataFound(
-                            mTrackIndex, mScratchParsableByteArrayAdapter);
-                }
-            } catch (IOException e) {
-                // Unexpected.
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private static final class DataReaderAdapter implements InputReader {
-
-        private DataReader mDataReader;
-        private int mCurrentPosition;
-        private long mLength;
-
-        public void setDataReader(DataReader dataReader, long length) {
-            mDataReader = dataReader;
-            mCurrentPosition = 0;
-            mLength = length;
-        }
-
-        // Input implementation.
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) throws IOException {
-            int readBytes = 0;
-            readBytes = mDataReader.read(buffer, offset, readLength);
-            mCurrentPosition += readBytes;
-            return readBytes;
-        }
-
-        @Override
-        public long getPosition() {
-            return mCurrentPosition;
-        }
-
-        @Override
-        public long getLength() {
-            return mLength - mCurrentPosition;
-        }
-    }
-
-    private static final class ParsableByteArrayAdapter implements InputReader {
-
-        private ParsableByteArray mByteArray;
-        private long mLength;
-        private int mCurrentPosition;
-
-        public void resetWithByteArray(ParsableByteArray byteArray, long length) {
-            mByteArray = byteArray;
-            mCurrentPosition = 0;
-            mLength = length;
-        }
-
-        // Input implementation.
-
-        @Override
-        public int read(byte[] buffer, int offset, int readLength) {
-            mByteArray.readBytes(buffer, offset, readLength);
-            mCurrentPosition += readLength;
-            return readLength;
-        }
-
-        @Override
-        public long getPosition() {
-            return mCurrentPosition;
-        }
-
-        @Override
-        public long getLength() {
-            return mLength - mCurrentPosition;
-        }
-    }
-
-    private static final class DummyExoPlayerSeekMap
-            implements com.google.android.exoplayer2.extractor.SeekMap {
-
-        @Override
-        public boolean isSeekable() {
-            return true;
-        }
-
-        @Override
-        public long getDurationUs() {
-            return C.TIME_UNSET;
-        }
-
-        @Override
-        public SeekPoints getSeekPoints(long timeUs) {
-            com.google.android.exoplayer2.extractor.SeekPoint seekPoint =
-                    new com.google.android.exoplayer2.extractor.SeekPoint(
-                            timeUs, /* position= */ 0);
-            return new SeekPoints(seekPoint, seekPoint);
-        }
-    }
-
-    /** Creates extractor instances. */
-    private interface ExtractorFactory {
-
-        /** Returns a new extractor instance. */
-        Extractor createInstance();
-    }
-
-    // Private static methods.
-
-    private static Format toExoPlayerCaptionFormat(MediaFormat mediaFormat) {
-        Format.Builder formatBuilder =
-                new Format.Builder().setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME));
-        if (mediaFormat.containsKey(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)) {
-            formatBuilder.setAccessibilityChannel(
-                    mediaFormat.getInteger(MediaFormat.KEY_CAPTION_SERVICE_NUMBER));
-        }
-        return formatBuilder.build();
-    }
-
-    private static MediaFormat toMediaFormat(Format format) {
-        MediaFormat result = new MediaFormat();
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
-
-        ColorInfo colorInfo = format.colorInfo;
-        if (colorInfo != null) {
-            setOptionalMediaFormatInt(
-                    result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
-            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange);
-            setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace);
-
-            if (format.colorInfo.hdrStaticInfo != null) {
-                result.setByteBuffer(
-                        MediaFormat.KEY_HDR_STATIC_INFO,
-                        ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
-            }
-        }
-
-        setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType);
-        setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs);
-        if (format.frameRate != Format.NO_VALUE) {
-            result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
-        }
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height);
-
-        List<byte[]> initData = format.initializationData;
-        for (int i = 0; i < initData.size(); i++) {
-            result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
-        }
-        setPcmEncoding(format, result);
-        setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
-        setOptionalMediaFormatInt(
-                result, MediaFormat.KEY_CAPTION_SERVICE_NUMBER, format.accessibilityChannel);
-
-        int selectionFlags = format.selectionFlags;
-        result.setInteger(
-                MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT);
-        result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT);
-        result.setInteger(
-                MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED);
-
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
-        setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
-
-        if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) {
-            int parWidth = 1;
-            int parHeight = 1;
-            if (format.pixelWidthHeightRatio < 1.0f) {
-                parHeight = 1 << 30;
-                parWidth = (int) (format.pixelWidthHeightRatio * parHeight);
-            } else if (format.pixelWidthHeightRatio > 1.0f) {
-                parWidth = 1 << 30;
-                parHeight = (int) (parWidth / format.pixelWidthHeightRatio);
-            }
-            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth);
-            result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight);
-            result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio);
-        }
-        if (format.drmInitData != null) {
-            // The crypto mode is propagated along with sample metadata. We also include it in the
-            // format for convenient use from ExoPlayer.
-            result.setString("crypto-mode-fourcc", format.drmInitData.schemeType);
-        }
-        if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
-            result.setLong("subsample-offset-us-long", format.subsampleOffsetUs);
-        }
-        // LACK OF SUPPORT FOR:
-        //    format.id;
-        //    format.metadata;
-        //    format.stereoMode;
-        return result;
-    }
-
-    private static ByteBuffer toByteBuffer(long[] longArray) {
-        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(longArray.length * Long.BYTES);
-        for (long element : longArray) {
-            byteBuffer.putLong(element);
-        }
-        byteBuffer.flip();
-        return byteBuffer;
-    }
-
-    private static ByteBuffer toByteBuffer(int[] intArray) {
-        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * Integer.BYTES);
-        for (int element : intArray) {
-            byteBuffer.putInt(element);
-        }
-        byteBuffer.flip();
-        return byteBuffer;
-    }
-
-    private static String toTypeString(int type) {
-        switch (type) {
-            case C.TRACK_TYPE_VIDEO:
-                return "video";
-            case C.TRACK_TYPE_AUDIO:
-                return "audio";
-            case C.TRACK_TYPE_TEXT:
-                return "text";
-            case C.TRACK_TYPE_METADATA:
-                return "metadata";
-            default:
-                return "unknown";
-        }
-    }
-
-    private static void setPcmEncoding(Format format, MediaFormat result) {
-        int exoPcmEncoding = format.pcmEncoding;
-        setOptionalMediaFormatInt(result, "exo-pcm-encoding", format.pcmEncoding);
-        int mediaFormatPcmEncoding;
-        switch (exoPcmEncoding) {
-            case C.ENCODING_PCM_8BIT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_8BIT;
-                break;
-            case C.ENCODING_PCM_16BIT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_16BIT;
-                break;
-            case C.ENCODING_PCM_FLOAT:
-                mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT;
-                break;
-            default:
-                // No matching value. Do nothing.
-                return;
-        }
-        result.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding);
-    }
-
-    private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) {
-        if (value != Format.NO_VALUE) {
-            mediaFormat.setInteger(key, value);
-        }
-    }
-
-    private static void setOptionalMediaFormatString(
-            MediaFormat mediaFormat, String key, @Nullable String value) {
-        if (value != null) {
-            mediaFormat.setString(key, value);
-        }
-    }
-
-    private DrmInitData toFrameworkDrmInitData(
-            com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) {
-        try {
-            return exoDrmInitData != null && mSchemeInitDataConstructor != null
-                    ? new MediaParserDrmInitData(exoDrmInitData)
-                    : null;
-        } catch (Throwable e) {
-            if (!mLoggedSchemeInitDataCreationException) {
-                mLoggedSchemeInitDataCreationException = true;
-                Log.e(TAG, "Unable to create SchemeInitData instance.");
-            }
-            return null;
-        }
-    }
-
-    /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
-    private static SeekPoint toSeekPoint(
-            com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
-        return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position);
-    }
-
-    /**
-     * Introduces random error to the given metric value in order to prevent the identification of
-     * the parsed media.
-     */
-    private static long addDither(long value) {
-        // Generate a random in [0, 1].
-        double randomDither = ThreadLocalRandom.current().nextFloat();
-        // Clamp the random number to [0, 2 * MEDIAMETRICS_DITHER].
-        randomDither *= 2 * MEDIAMETRICS_DITHER;
-        // Translate the random number to [1 - MEDIAMETRICS_DITHER, 1 + MEDIAMETRICS_DITHER].
-        randomDither += 1 - MEDIAMETRICS_DITHER;
-        return value != -1 ? (long) (value * randomDither) : -1;
-    }
-
-    private static void assertValidNames(@NonNull String[] names) {
-        for (String name : names) {
-            if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) {
-                throw new IllegalArgumentException(
-                        "Invalid extractor name: "
-                                + name
-                                + ". Supported parsers are: "
-                                + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet())
-                                + ".");
-            }
-        }
-    }
-
-    private int getMediaParserFlags(int flags) {
-        @SampleFlags int result = 0;
-        result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0;
-        result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0;
-        result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0;
-        result |=
-                (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0 && mIncludeSupplementalData
-                        ? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA
-                        : 0;
-        result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0;
-        return result;
-    }
-
-    @Nullable
-    private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() {
-        // TODO: Use constructor statically when available.
-        Constructor<DrmInitData.SchemeInitData> constructor;
-        try {
-            return DrmInitData.SchemeInitData.class.getConstructor(
-                    UUID.class, String.class, byte[].class);
-        } catch (Throwable e) {
-            Log.e(TAG, "Unable to get SchemeInitData constructor.");
-            return null;
-        }
-    }
-
-    // Native methods.
-
-    private native void nativeSubmitMetrics(
-            String logSessionId,
-            String parserName,
-            boolean createdByName,
-            String parserPool,
-            String lastObservedExceptionName,
-            long resourceByteCount,
-            long durationMillis,
-            String trackMimeTypes,
-            String trackCodecs,
-            String alteredParameters,
-            int videoWidth,
-            int videoHeight);
-
-    // Static initialization.
-
-    static {
-        System.loadLibrary(JNI_LIBRARY_NAME);
-
-        // Using a LinkedHashMap to keep the insertion order when iterating over the keys.
-        LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
-        // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
-        // which in turn aims to minimize the chances of incorrect extractor selections.
-        extractorFactoriesByName.put(PARSER_NAME_MATROSKA, MatroskaExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FMP4, FragmentedMp4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_MP4, Mp4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_MP3, Mp3Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_ADTS, AdtsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AC3, Ac3Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_TS, TsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FLV, FlvExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_OGG, OggExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_PS, PsExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_WAV, WavExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AMR, AmrExtractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_AC4, Ac4Extractor::new);
-        extractorFactoriesByName.put(PARSER_NAME_FLAC, FlacExtractor::new);
-        EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
-
-        HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
-        expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class);
-        expectedTypeByParameterName.put(
-                PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class);
-        expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class);
-        // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
-        // instead. Checking that the value is a List is insufficient to catch wrong parameter
-        // value types.
-        int sumOfParameterNameLengths =
-                expectedTypeByParameterName.keySet().stream()
-                        .map(String::length)
-                        .reduce(0, Integer::sum);
-        sumOfParameterNameLengths += PARAMETER_EXPOSE_CAPTION_FORMATS.length();
-        // Add space for any required separators.
-        MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH =
-                sumOfParameterNameLengths + expectedTypeByParameterName.size();
-
-        EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
deleted file mode 100644
index 7d07eb3..0000000
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ /dev/null
@@ -1,932 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-import static android.media.MediaConstants.KEY_PACKAGE_NAME;
-import static android.media.MediaConstants.KEY_PID;
-import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE;
-import static android.media.MediaConstants.KEY_SESSION2LINK;
-import static android.media.MediaConstants.KEY_TOKEN_EXTRAS;
-import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR;
-import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED;
-import static android.media.Session2Token.TYPE_SESSION;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.BadParcelableException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.Process;
-import android.os.ResultReceiver;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.modules.utils.build.SdkLevel;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps.
- */
-public class MediaSession2 implements AutoCloseable {
-    static final String TAG = "MediaSession2";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // Note: This checks the uniqueness of a session ID only in a single process.
-    // When the framework becomes able to check the uniqueness, this logic should be removed.
-    //@GuardedBy("MediaSession.class")
-    private static final List<String> SESSION_ID_LIST = new ArrayList<>();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Map<Controller2Link, ControllerInfo> mConnectedControllers = new HashMap<>();
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Context mContext;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Executor mCallbackExecutor;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final SessionCallback mCallback;
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Session2Link mSessionStub;
-
-    private final String mSessionId;
-    private final PendingIntent mSessionActivity;
-    private final Session2Token mSessionToken;
-    private final MediaSessionManager mMediaSessionManager;
-    private final MediaCommunicationManager mCommunicationManager;
-    private final Handler mResultHandler;
-
-    //@GuardedBy("mLock")
-    private boolean mClosed;
-    //@GuardedBy("mLock")
-    private boolean mPlaybackActive;
-    //@GuardedBy("mLock")
-    private ForegroundServiceEventCallback mForegroundServiceEventCallback;
-
-    MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
-            @NonNull Executor callbackExecutor, @NonNull SessionCallback callback,
-            @NonNull Bundle tokenExtras) {
-        synchronized (MediaSession2.class) {
-            if (SESSION_ID_LIST.contains(id)) {
-                throw new IllegalStateException("Session ID must be unique. ID=" + id);
-            }
-            SESSION_ID_LIST.add(id);
-        }
-
-        mContext = context;
-        mSessionId = id;
-        mSessionActivity = sessionActivity;
-        mCallbackExecutor = callbackExecutor;
-        mCallback = callback;
-        mSessionStub = new Session2Link(this);
-        mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(),
-                mSessionStub, tokenExtras);
-        if (SdkLevel.isAtLeastS()) {
-            mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class);
-            mMediaSessionManager = null;
-        } else {
-            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
-            mCommunicationManager = null;
-        }
-        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
-        mResultHandler = new Handler(context.getMainLooper());
-        mClosed = false;
-    }
-
-    @Override
-    public void close() {
-        try {
-            List<ControllerInfo> controllerInfos;
-            ForegroundServiceEventCallback callback;
-            synchronized (mLock) {
-                if (mClosed) {
-                    return;
-                }
-                mClosed = true;
-                controllerInfos = getConnectedControllers();
-                mConnectedControllers.clear();
-                callback = mForegroundServiceEventCallback;
-                mForegroundServiceEventCallback = null;
-            }
-            synchronized (MediaSession2.class) {
-                SESSION_ID_LIST.remove(mSessionId);
-            }
-            if (callback != null) {
-                callback.onSessionClosed(this);
-            }
-            for (ControllerInfo info : controllerInfos) {
-                info.notifyDisconnected();
-            }
-        } catch (Exception e) {
-            // Should not be here.
-        }
-    }
-
-    /**
-     * Returns the session ID
-     */
-    @NonNull
-    public String getId() {
-        return mSessionId;
-    }
-
-    /**
-     * Returns the {@link Session2Token} for creating {@link MediaController2}.
-     */
-    @NonNull
-    public Session2Token getToken() {
-        return mSessionToken;
-    }
-
-    /**
-     * Broadcasts a session command to all the connected controllers
-     * <p>
-     * @param command the session command
-     * @param args optional arguments
-     */
-    public void broadcastSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        List<ControllerInfo> controllerInfos = getConnectedControllers();
-        for (ControllerInfo controller : controllerInfos) {
-            controller.sendSessionCommand(command, args, null);
-        }
-    }
-
-    /**
-     * Sends a session command to a specific controller
-     * <p>
-     * @param controller the controller to get the session command
-     * @param command the session command
-     * @param args optional arguments
-     * @return a token which will be sent together in {@link SessionCallback#onCommandResult}
-     *     when its result is received.
-     */
-    @NonNull
-    public Object sendSessionCommand(@NonNull ControllerInfo controller,
-            @NonNull Session2Command command, @Nullable Bundle args) {
-        if (controller == null) {
-            throw new IllegalArgumentException("controller shouldn't be null");
-        }
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                controller.receiveCommandResult(this);
-                mCallbackExecutor.execute(() -> {
-                    mCallback.onCommandResult(MediaSession2.this, controller, this,
-                            command, new Session2Command.Result(resultCode, resultData));
-                });
-            }
-        };
-        controller.sendSessionCommand(command, args, resultReceiver);
-        return resultReceiver;
-    }
-
-    /**
-     * Cancels the session command previously sent.
-     *
-     * @param controller the controller to get the session command
-     * @param token the token which is returned from {@link #sendSessionCommand}.
-     */
-    public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) {
-        if (controller == null) {
-            throw new IllegalArgumentException("controller shouldn't be null");
-        }
-        if (token == null) {
-            throw new IllegalArgumentException("token shouldn't be null");
-        }
-        controller.cancelSessionCommand(token);
-    }
-
-    /**
-     * Sets whether the playback is active (i.e. playing something)
-     *
-     * @param playbackActive {@code true} if the playback active, {@code false} otherwise.
-     **/
-    public void setPlaybackActive(boolean playbackActive) {
-        final ForegroundServiceEventCallback serviceCallback;
-        synchronized (mLock) {
-            if (mPlaybackActive == playbackActive) {
-                return;
-            }
-            mPlaybackActive = playbackActive;
-            serviceCallback = mForegroundServiceEventCallback;
-        }
-        if (serviceCallback != null) {
-            serviceCallback.onPlaybackActiveChanged(this, playbackActive);
-        }
-        List<ControllerInfo> controllerInfos = getConnectedControllers();
-        for (ControllerInfo controller : controllerInfos) {
-            controller.notifyPlaybackActiveChanged(playbackActive);
-        }
-    }
-
-    /**
-     * Returns whether the playback is active (i.e. playing something)
-     *
-     * @return {@code true} if the playback active, {@code false} otherwise.
-     */
-    public boolean isPlaybackActive() {
-        synchronized (mLock) {
-            return mPlaybackActive;
-        }
-    }
-
-    /**
-     * Gets the list of the connected controllers
-     *
-     * @return list of the connected controllers.
-     */
-    @NonNull
-    public List<ControllerInfo> getConnectedControllers() {
-        List<ControllerInfo> controllers = new ArrayList<>();
-        synchronized (mLock) {
-            controllers.addAll(mConnectedControllers.values());
-        }
-        return controllers;
-    }
-
-    /**
-     * Returns whether the given bundle includes non-framework Parcelables.
-     */
-    static boolean hasCustomParcelable(@Nullable Bundle bundle) {
-        if (bundle == null) {
-            return false;
-        }
-
-        // Try writing the bundle to parcel, and read it with framework classloader.
-        Parcel parcel = null;
-        try {
-            parcel = Parcel.obtain();
-            parcel.writeBundle(bundle);
-            parcel.setDataPosition(0);
-            Bundle out = parcel.readBundle(null);
-
-            for (String key : out.keySet()) {
-                out.get(key);
-            }
-        } catch (BadParcelableException e) {
-            Log.d(TAG, "Custom parcelable in bundle.", e);
-            return true;
-        } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
-        }
-        return false;
-    }
-
-    boolean isClosed() {
-        synchronized (mLock) {
-            return mClosed;
-        }
-    }
-
-    SessionCallback getCallback() {
-        return mCallback;
-    }
-
-    boolean isTrustedForMediaControl(RemoteUserInfo remoteUserInfo) {
-        if (SdkLevel.isAtLeastS()) {
-            return mCommunicationManager.isTrustedForMediaControl(remoteUserInfo);
-        } else {
-            return mMediaSessionManager.isTrustedForMediaControl(remoteUserInfo);
-        }
-    }
-
-    void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
-        synchronized (mLock) {
-            if (mForegroundServiceEventCallback == callback) {
-                return;
-            }
-            if (mForegroundServiceEventCallback != null && callback != null) {
-                throw new IllegalStateException("A session cannot be added to multiple services");
-            }
-            mForegroundServiceEventCallback = callback;
-        }
-    }
-
-    // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
-    void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
-            Bundle connectionRequest) {
-        if (callingPid == 0) {
-            // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from
-            // the remote process. If it's the case, use PID from the connectionRequest.
-            callingPid = connectionRequest.getInt(KEY_PID);
-        }
-        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-
-        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid);
-
-        Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS);
-        if (connectionHints == null) {
-            Log.w(TAG, "connectionHints shouldn't be null.");
-            connectionHints = Bundle.EMPTY;
-        } else if (hasCustomParcelable(connectionHints)) {
-            Log.w(TAG, "connectionHints contain custom parcelable. Ignoring.");
-            connectionHints = Bundle.EMPTY;
-        }
-
-        final ControllerInfo controllerInfo = new ControllerInfo(
-                remoteUserInfo,
-                isTrustedForMediaControl(remoteUserInfo),
-                controller,
-                connectionHints);
-        mCallbackExecutor.execute(() -> {
-            boolean connected = false;
-            try {
-                if (isClosed()) {
-                    return;
-                }
-                controllerInfo.mAllowedCommands =
-                        mCallback.onConnect(MediaSession2.this, controllerInfo);
-                // Don't reject connection for the request from trusted app.
-                // Otherwise server will fail to retrieve session's information to dispatch
-                // media keys to.
-                if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) {
-                    return;
-                }
-                if (controllerInfo.mAllowedCommands == null) {
-                    // For trusted apps, send non-null allowed commands to keep
-                    // connection.
-                    controllerInfo.mAllowedCommands =
-                            new Session2CommandGroup.Builder().build();
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "Accepting connection: " + controllerInfo);
-                }
-                // If connection is accepted, notify the current state to the controller.
-                // It's needed because we cannot call synchronous calls between
-                // session/controller.
-                Bundle connectionResult = new Bundle();
-                connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub);
-                connectionResult.putParcelable(KEY_ALLOWED_COMMANDS,
-                        controllerInfo.mAllowedCommands);
-                connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive());
-                connectionResult.putBundle(KEY_TOKEN_EXTRAS, mSessionToken.getExtras());
-
-                // Double check if session is still there, because close() can be called in
-                // another thread.
-                if (isClosed()) {
-                    return;
-                }
-                controllerInfo.notifyConnected(connectionResult);
-                synchronized (mLock) {
-                    if (mConnectedControllers.containsKey(controller)) {
-                        Log.w(TAG, "Controller " + controllerInfo + " has sent connection"
-                                + " request multiple times");
-                    }
-                    mConnectedControllers.put(controller, controllerInfo);
-                }
-                mCallback.onPostConnect(MediaSession2.this, controllerInfo);
-                connected = true;
-            } finally {
-                if (!connected || isClosed()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Rejecting connection or notifying that session is closed"
-                                + ", controllerInfo=" + controllerInfo);
-                    }
-                    synchronized (mLock) {
-                        mConnectedControllers.remove(controller);
-                    }
-                    controllerInfo.notifyDisconnected();
-                }
-            }
-        });
-    }
-
-    // Called by Session2Link.onDisconnect
-    void onDisconnect(@NonNull final Controller2Link controller, int seq) {
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.remove(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-        mCallbackExecutor.execute(() -> {
-            mCallback.onDisconnected(MediaSession2.this, controllerInfo);
-        });
-    }
-
-    // Called by Session2Link.onSessionCommand
-    void onSessionCommand(@NonNull final Controller2Link controller, final int seq,
-            final Session2Command command, final Bundle args,
-            @Nullable ResultReceiver resultReceiver) {
-        if (controller == null) {
-            return;
-        }
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-
-        // TODO: check allowed commands.
-        synchronized (mLock) {
-            controllerInfo.addRequestedCommandSeqNumber(seq);
-        }
-        mCallbackExecutor.execute(() -> {
-            if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) {
-                if (resultReceiver != null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                }
-                return;
-            }
-            Session2Command.Result result = mCallback.onSessionCommand(
-                    MediaSession2.this, controllerInfo, command, args);
-            if (resultReceiver != null) {
-                if (result == null) {
-                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
-                } else {
-                    resultReceiver.send(result.getResultCode(), result.getResultData());
-                }
-            }
-        });
-    }
-
-    // Called by Session2Link.onCancelCommand
-    void onCancelCommand(@NonNull final Controller2Link controller, final int seq) {
-        final ControllerInfo controllerInfo;
-        synchronized (mLock) {
-            controllerInfo = mConnectedControllers.get(controller);
-        }
-        if (controllerInfo == null) {
-            return;
-        }
-        controllerInfo.removeRequestedCommandSeqNumber(seq);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builder for {@link MediaSession2}.
-     * <p>
-     * Any incoming event from the {@link MediaController2} will be handled on the callback
-     * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default.
-     */
-    public static final class Builder {
-        private Context mContext;
-        private String mId;
-        private PendingIntent mSessionActivity;
-        private Executor mCallbackExecutor;
-        private SessionCallback mCallback;
-        private Bundle mExtras;
-
-        /**
-         * Creates a builder for {@link MediaSession2}.
-         *
-         * @param context Context
-         * @throws IllegalArgumentException if context is {@code null}.
-         */
-        public Builder(@NonNull Context context) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            mContext = context;
-        }
-
-        /**
-         * Set an intent for launching UI for this Session. This can be used as a
-         * quick link to an ongoing media screen. The intent should be for an
-         * activity that may be started using {@link Context#startActivity(Intent)}.
-         *
-         * @param pi The intent to launch to show UI for this session.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setSessionActivity(@Nullable PendingIntent pi) {
-            mSessionActivity = pi;
-            return this;
-        }
-
-        /**
-         * Set ID of the session. If it's not set, an empty string will be used to create a session.
-         * <p>
-         * Use this if and only if your app supports multiple playback at the same time and also
-         * wants to provide external apps to have finer controls of them.
-         *
-         * @param id id of the session. Must be unique per package.
-         * @throws IllegalArgumentException if id is {@code null}.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setId(@NonNull String id) {
-            if (id == null) {
-                throw new IllegalArgumentException("id shouldn't be null");
-            }
-            mId = id;
-            return this;
-        }
-
-        /**
-         * Set callback for the session and its executor.
-         *
-         * @param executor callback executor
-         * @param callback session callback.
-         * @return The Builder to allow chaining
-         */
-        @NonNull
-        public Builder setSessionCallback(@NonNull Executor executor,
-                @NonNull SessionCallback callback) {
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return this;
-        }
-
-        /**
-         * Set extras for the session token. If null or not set, {@link Session2Token#getExtras()}
-         * will return an empty {@link Bundle}. An {@link IllegalArgumentException} will be thrown
-         * if the bundle contains any non-framework Parcelable objects.
-         *
-         * @return The Builder to allow chaining
-         * @see Session2Token#getExtras()
-         */
-        @NonNull
-        public Builder setExtras(@NonNull Bundle extras) {
-            if (extras == null) {
-                throw new NullPointerException("extras shouldn't be null");
-            }
-            if (hasCustomParcelable(extras)) {
-                throw new IllegalArgumentException(
-                        "extras shouldn't contain any custom parcelables");
-            }
-            mExtras = new Bundle(extras);
-            return this;
-        }
-
-        /**
-         * Build {@link MediaSession2}.
-         *
-         * @return a new session
-         * @throws IllegalStateException if the session with the same id is already exists for the
-         *      package.
-         */
-        @NonNull
-        public MediaSession2 build() {
-            if (mCallbackExecutor == null) {
-                mCallbackExecutor = mContext.getMainExecutor();
-            }
-            if (mCallback == null) {
-                mCallback = new SessionCallback() {};
-            }
-            if (mId == null) {
-                mId = "";
-            }
-            if (mExtras == null) {
-                mExtras = Bundle.EMPTY;
-            }
-            MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
-                    mCallbackExecutor, mCallback, mExtras);
-
-            // Notify framework about the newly create session after the constructor is finished.
-            // Otherwise, framework may access the session before the initialization is finished.
-            try {
-                if (SdkLevel.isAtLeastS()) {
-                    MediaCommunicationManager manager =
-                            mContext.getSystemService(MediaCommunicationManager.class);
-                    manager.notifySession2Created(session2.getToken());
-                } else {
-                    MediaSessionManager manager =
-                            mContext.getSystemService(MediaSessionManager.class);
-                    manager.notifySession2Created(session2.getToken());
-                }
-            } catch (Exception e) {
-                session2.close();
-                throw e;
-            }
-
-            return session2;
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Information of a controller.
-     */
-    public static final class ControllerInfo {
-        private final RemoteUserInfo mRemoteUserInfo;
-        private final boolean mIsTrusted;
-        private final Controller2Link mControllerBinder;
-        private final Bundle mConnectionHints;
-        private final Object mLock = new Object();
-        //@GuardedBy("mLock")
-        private int mNextSeqNumber;
-        //@GuardedBy("mLock")
-        private ArrayMap<ResultReceiver, Integer> mPendingCommands;
-        //@GuardedBy("mLock")
-        private ArraySet<Integer> mRequestedCommandSeqNumbers;
-
-        @SuppressWarnings("WeakerAccess") /* synthetic access */
-        Session2CommandGroup mAllowedCommands;
-
-        /**
-         * @param remoteUserInfo remote user info
-         * @param trusted {@code true} if trusted, {@code false} otherwise
-         * @param controllerBinder Controller2Link for the connected controller.
-         * @param connectionHints a session-specific argument sent from the controller for the
-         *                        connection. The contents of this bundle may affect the
-         *                        connection result.
-         */
-        ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted,
-                @Nullable Controller2Link controllerBinder, @NonNull Bundle connectionHints) {
-            mRemoteUserInfo = remoteUserInfo;
-            mIsTrusted = trusted;
-            mControllerBinder = controllerBinder;
-            mConnectionHints = connectionHints;
-            mPendingCommands = new ArrayMap<>();
-            mRequestedCommandSeqNumbers = new ArraySet<>();
-        }
-
-        /**
-         * @return remote user info of the controller.
-         */
-        @NonNull
-        public RemoteUserInfo getRemoteUserInfo() {
-            return mRemoteUserInfo;
-        }
-
-        /**
-         * @return package name of the controller.
-         */
-        @NonNull
-        public String getPackageName() {
-            return mRemoteUserInfo.getPackageName();
-        }
-
-        /**
-         * @return uid of the controller. Can be a negative value if the uid cannot be obtained.
-         */
-        public int getUid() {
-            return mRemoteUserInfo.getUid();
-        }
-
-        /**
-         * @return connection hints sent from controller.
-         */
-        @NonNull
-        public Bundle getConnectionHints() {
-            return new Bundle(mConnectionHints);
-        }
-
-        /**
-         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
-         * has a enabled notification listener so can be trusted to accept connection and incoming
-         * command request.
-         *
-         * @return {@code true} if the controller is trusted.
-         * @hide
-         */
-        public boolean isTrusted() {
-            return mIsTrusted;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mControllerBinder, mRemoteUserInfo);
-        }
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (!(obj instanceof ControllerInfo)) return false;
-            if (this == obj) return true;
-
-            ControllerInfo other = (ControllerInfo) obj;
-            if (mControllerBinder != null || other.mControllerBinder != null) {
-                return Objects.equals(mControllerBinder, other.mControllerBinder);
-            }
-            return mRemoteUserInfo.equals(other.mRemoteUserInfo);
-        }
-
-        @Override
-        @NonNull
-        public String toString() {
-            return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid="
-                    + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})";
-        }
-
-        void notifyConnected(Bundle connectionResult) {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyConnected(getNextSeqNumber(), connectionResult);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void notifyDisconnected() {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyDisconnected(getNextSeqNumber());
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void notifyPlaybackActiveChanged(boolean playbackActive) {
-            if (mControllerBinder == null) return;
-
-            try {
-                mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-            }
-        }
-
-        void sendSessionCommand(Session2Command command, Bundle args,
-                ResultReceiver resultReceiver) {
-            if (mControllerBinder == null) return;
-
-            try {
-                int seq = getNextSeqNumber();
-                synchronized (mLock) {
-                    mPendingCommands.put(resultReceiver, seq);
-                }
-                mControllerBinder.sendSessionCommand(seq, command, args, resultReceiver);
-            } catch (RuntimeException e) {
-                // Controller may be died prematurely.
-                synchronized (mLock) {
-                    mPendingCommands.remove(resultReceiver);
-                }
-                resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
-            }
-        }
-
-        void cancelSessionCommand(@NonNull Object token) {
-            if (mControllerBinder == null) return;
-            Integer seq;
-            synchronized (mLock) {
-                seq = mPendingCommands.remove(token);
-            }
-            if (seq != null) {
-                mControllerBinder.cancelSessionCommand(seq);
-            }
-        }
-
-        void receiveCommandResult(ResultReceiver resultReceiver) {
-            synchronized (mLock) {
-                mPendingCommands.remove(resultReceiver);
-            }
-        }
-
-        void addRequestedCommandSeqNumber(int seq) {
-            synchronized (mLock) {
-                mRequestedCommandSeqNumbers.add(seq);
-            }
-        }
-
-        boolean removeRequestedCommandSeqNumber(int seq) {
-            synchronized (mLock) {
-                return mRequestedCommandSeqNumbers.remove(seq);
-            }
-        }
-
-        private int getNextSeqNumber() {
-            synchronized (mLock) {
-                return mNextSeqNumber++;
-            }
-        }
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Callback to be called for all incoming commands from {@link MediaController2}s.
-     */
-    public abstract static class SessionCallback {
-        /**
-         * Called when a controller is created for this session. Return allowed commands for
-         * controller. By default it returns {@code null}.
-         * <p>
-         * You can reject the connection by returning {@code null}. In that case, controller
-         * receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)}
-         * and cannot be used.
-         * <p>
-         * The controller hasn't connected yet in this method, so calls to the controller
-         * (e.g. {@link #sendSessionCommand}) would be ignored. Override {@link #onPostConnect} for
-         * the custom initialization for the controller instead.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         * @return allowed commands. Can be {@code null} to reject connection.
-         */
-        @Nullable
-        public Session2CommandGroup onConnect(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {
-            return null;
-        }
-
-        /**
-         * Called immediately after a controller is connected. This is a convenient method to add
-         * custom initialization between the session and a controller.
-         * <p>
-         * Note that calls to the controller (e.g. {@link #sendSessionCommand}) work here but don't
-         * work in {@link #onConnect} because the controller hasn't connected yet in
-         * {@link #onConnect}.
-         *
-         * @param session the session for this event
-         * @param controller controller information.
-         */
-        public void onPostConnect(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {
-        }
-
-        /**
-         * Called when a controller is disconnected
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         */
-        public void onDisconnected(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) {}
-
-        /**
-         * Called when a controller sent a session command.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param command the session command
-         * @param args optional arguments
-         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
-         *         will be sent to the session.
-         */
-        @Nullable
-        public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Session2Command command,
-                @Nullable Bundle args) {
-            return null;
-        }
-
-        /**
-         * Called when the command sent to the controller is finished.
-         *
-         * @param session the session for this event
-         * @param controller controller information
-         * @param token the token got from {@link MediaSession2#sendSessionCommand}
-         * @param command the session command
-         * @param result the result of the session command
-         */
-        public void onCommandResult(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
-    }
-
-    abstract static class ForegroundServiceEventCallback {
-        public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {}
-        public void onSessionClosed(MediaSession2 session) {}
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
deleted file mode 100644
index 9f80c43..0000000
--- a/apex/media/framework/java/android/media/MediaSession2Service.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaConstants.KEY_CONNECTION_HINTS;
-import static android.media.MediaConstants.KEY_PACKAGE_NAME;
-import static android.media.MediaConstants.KEY_PID;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Service containing {@link MediaSession2}.
- */
-public abstract class MediaSession2Service extends Service {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     */
-    public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
-
-    private static final String TAG = "MediaSession2Service";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final MediaSession2.ForegroundServiceEventCallback mForegroundServiceEventCallback =
-            new MediaSession2.ForegroundServiceEventCallback() {
-                @Override
-                public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
-                    MediaSession2Service.this.onPlaybackActiveChanged(session, playbackActive);
-                }
-
-                @Override
-                public void onSessionClosed(MediaSession2 session) {
-                    removeSession(session);
-                }
-            };
-
-    private final Object mLock = new Object();
-    //@GuardedBy("mLock")
-    private NotificationManager mNotificationManager;
-    //@GuardedBy("mLock")
-    private MediaSessionManager mMediaSessionManager;
-    //@GuardedBy("mLock")
-    private Intent mStartSelfIntent;
-    //@GuardedBy("mLock")
-    private Map<String, MediaSession2> mSessions = new ArrayMap<>();
-    //@GuardedBy("mLock")
-    private Map<MediaSession2, MediaNotification> mNotifications = new ArrayMap<>();
-    //@GuardedBy("mLock")
-    private MediaSession2ServiceStub mStub;
-
-    /**
-     * Called by the system when the service is first created. Do not call this method directly.
-     * <p>
-     * Override this method if you need your own initialization. Derived classes MUST call through
-     * to the super class's implementation of this method.
-     */
-    @CallSuper
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        synchronized (mLock) {
-            mStub = new MediaSession2ServiceStub(this);
-            mStartSelfIntent = new Intent(this, this.getClass());
-            mNotificationManager =
-                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-            mMediaSessionManager =
-                    (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
-        }
-    }
-
-    @CallSuper
-    @Override
-    @Nullable
-    public IBinder onBind(@NonNull Intent intent) {
-        if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            synchronized (mLock) {
-                return mStub;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Called by the system to notify that it is no longer used and is being removed. Do not call
-     * this method directly.
-     * <p>
-     * Override this method if you need your own clean up. Derived classes MUST call through
-     * to the super class's implementation of this method.
-     */
-    @CallSuper
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        synchronized (mLock) {
-            List<MediaSession2> sessions = getSessions();
-            for (MediaSession2 session : sessions) {
-                removeSession(session);
-            }
-            mSessions.clear();
-            mNotifications.clear();
-        }
-        mStub.close();
-    }
-
-    /**
-     * Called when a {@link MediaController2} is created with the this service's
-     * {@link Session2Token}. Return the session for telling the controller which session to
-     * connect. Return {@code null} to reject the connection from this controller.
-     * <p>
-     * Session returned here will be added to this service automatically. You don't need to call
-     * {@link #addSession(MediaSession2)} for that.
-     * <p>
-     * This method is always called on the main thread.
-     *
-     * @param controllerInfo information of the controller which is trying to connect.
-     * @return a {@link MediaSession2} instance for the controller to connect to, or {@code null}
-     *         to reject connection
-     * @see MediaSession2.Builder
-     * @see #getSessions()
-     */
-    @Nullable
-    public abstract MediaSession2 onGetSession(@NonNull ControllerInfo controllerInfo);
-
-    /**
-     * Called to update the media notification when the playback state changes.
-     * <p>
-     * If playback is active and a notification is returned, the service uses it to become a
-     * foreground service. If playback is not active then the notification is still posted, but the
-     * service does not become a foreground service.
-     * <p>
-     * Apps must request the {@link android.Manifest.permission#FOREGROUND_SERVICE} permission
-     * in order to use this API. For apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU}
-     * or later, notifications will only be posted if the app has also been granted the
-     * {@link android.Manifest.permission#POST_NOTIFICATIONS} permission.
-     *
-     * @param session the session for which an updated media notification is required.
-     * @return the {@link MediaNotification}. Can be {@code null}.
-     */
-    @Nullable
-    public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
-
-    /**
-     * Adds a session to this service.
-     * <p>
-     * Added session will be removed automatically when it's closed, or removed when
-     * {@link #removeSession} is called.
-     *
-     * @param session a session to be added.
-     * @see #removeSession(MediaSession2)
-     */
-    public final void addSession(@NonNull MediaSession2 session) {
-        if (session == null) {
-            throw new IllegalArgumentException("session shouldn't be null");
-        }
-        if (session.isClosed()) {
-            throw new IllegalArgumentException("session is already closed");
-        }
-        synchronized (mLock) {
-            MediaSession2 previousSession = mSessions.get(session.getId());
-            if (previousSession != null) {
-                if (previousSession != session) {
-                    Log.w(TAG, "Session ID should be unique, ID=" + session.getId()
-                            + ", previous=" + previousSession + ", session=" + session);
-                }
-                return;
-            }
-            mSessions.put(session.getId(), session);
-            session.setForegroundServiceEventCallback(mForegroundServiceEventCallback);
-        }
-    }
-
-    /**
-     * Removes a session from this service.
-     *
-     * @param session a session to be removed.
-     * @see #addSession(MediaSession2)
-     */
-    public final void removeSession(@NonNull MediaSession2 session) {
-        if (session == null) {
-            throw new IllegalArgumentException("session shouldn't be null");
-        }
-        MediaNotification notification;
-        synchronized (mLock) {
-            if (mSessions.get(session.getId()) != session) {
-                // Session isn't added or removed already.
-                return;
-            }
-            mSessions.remove(session.getId());
-            notification = mNotifications.remove(session);
-        }
-        session.setForegroundServiceEventCallback(null);
-        if (notification != null) {
-            mNotificationManager.cancel(notification.getNotificationId());
-        }
-        if (getSessions().isEmpty()) {
-            stopForeground(false);
-        }
-    }
-
-    /**
-     * Gets the list of {@link MediaSession2}s that you've added to this service.
-     *
-     * @return sessions
-     */
-    public final @NonNull List<MediaSession2> getSessions() {
-        List<MediaSession2> list = new ArrayList<>();
-        synchronized (mLock) {
-            list.addAll(mSessions.values());
-        }
-        return list;
-    }
-
-    /**
-     * Returns the {@link MediaSessionManager}.
-     */
-    @NonNull
-    MediaSessionManager getMediaSessionManager() {
-        synchronized (mLock) {
-            return mMediaSessionManager;
-        }
-    }
-
-    /**
-     * Called by registered {@link MediaSession2.ForegroundServiceEventCallback}
-     *
-     * @param session session with change
-     * @param playbackActive {@code true} if playback is active.
-     */
-    void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
-        MediaNotification mediaNotification = onUpdateNotification(session);
-        if (mediaNotification == null) {
-            // The service implementation doesn't want to use the automatic start/stopForeground
-            // feature.
-            return;
-        }
-        synchronized (mLock) {
-            mNotifications.put(session, mediaNotification);
-        }
-        int id = mediaNotification.getNotificationId();
-        Notification notification = mediaNotification.getNotification();
-        if (!playbackActive) {
-            mNotificationManager.notify(id, notification);
-            return;
-        }
-        // playbackActive == true
-        startForegroundService(mStartSelfIntent);
-        startForeground(id, notification);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
-     * foreground service to keep playback running in the background. It's highly recommended to
-     * show media style notification here.
-     */
-    public static class MediaNotification {
-        private final int mNotificationId;
-        private final Notification mNotification;
-
-        /**
-         * Default constructor
-         *
-         * @param notificationId notification id to be used for
-         *        {@link NotificationManager#notify(int, Notification)}.
-         * @param notification a notification to make session service run in the foreground. Media
-         *        style notification is recommended here.
-         */
-        public MediaNotification(int notificationId, @NonNull Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("notification shouldn't be null");
-            }
-            mNotificationId = notificationId;
-            mNotification = notification;
-        }
-
-        /**
-         * Gets the id of the notification.
-         *
-         * @return the notification id
-         */
-        public int getNotificationId() {
-            return mNotificationId;
-        }
-
-        /**
-         * Gets the notification.
-         *
-         * @return the notification
-         */
-        @NonNull
-        public Notification getNotification() {
-            return mNotification;
-        }
-    }
-
-    private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
-            implements AutoCloseable {
-        final WeakReference<MediaSession2Service> mService;
-        final Handler mHandler;
-
-        MediaSession2ServiceStub(MediaSession2Service service) {
-            mService = new WeakReference<>(service);
-            mHandler = new Handler(service.getMainLooper());
-        }
-
-        @Override
-        public void connect(Controller2Link caller, int seq, Bundle connectionRequest) {
-            if (mService.get() == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Service is already destroyed");
-                }
-                return;
-            }
-            if (caller == null || connectionRequest == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller
-                            + ", connectionRequest=" + connectionRequest);
-                }
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mHandler.post(() -> {
-                    boolean shouldNotifyDisconnected = true;
-                    try {
-                        final MediaSession2Service service = mService.get();
-                        if (service == null) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Service isn't available");
-                            }
-                            return;
-                        }
-
-                        String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME);
-                        // The Binder.getCallingPid() can be 0 for an oneway call from the
-                        // remote process. If it's the case, use PID from the connectionRequest.
-                        RemoteUserInfo remoteUserInfo = new RemoteUserInfo(
-                                callingPkg,
-                                pid == 0 ? connectionRequest.getInt(KEY_PID) : pid,
-                                uid);
-
-                        Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS);
-                        if (connectionHints == null) {
-                            Log.w(TAG, "connectionHints shouldn't be null.");
-                            connectionHints = Bundle.EMPTY;
-                        } else if (MediaSession2.hasCustomParcelable(connectionHints)) {
-                            Log.w(TAG, "connectionHints contain custom parcelable. Ignoring.");
-                            connectionHints = Bundle.EMPTY;
-                        }
-
-                        final ControllerInfo controllerInfo = new ControllerInfo(
-                                remoteUserInfo,
-                                service.getMediaSessionManager()
-                                        .isTrustedForMediaControl(remoteUserInfo),
-                                caller,
-                                connectionHints);
-
-                        if (DEBUG) {
-                            Log.d(TAG, "Handling incoming connection request from the"
-                                    + " controller=" + controllerInfo);
-                        }
-
-                        final MediaSession2 session;
-                        session = service.onGetSession(controllerInfo);
-
-                        if (session == null) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Rejecting incoming connection request from the"
-                                        + " controller=" + controllerInfo);
-                            }
-                            // Note: Trusted controllers also can be rejected according to the
-                            // service implementation.
-                            return;
-                        }
-                        service.addSession(session);
-                        shouldNotifyDisconnected = false;
-                        session.onConnect(caller, pid, uid, seq, connectionRequest);
-                    } catch (Exception e) {
-                        // Don't propagate exception in service to the controller.
-                        Log.w(TAG, "Failed to add a session to session service", e);
-                    } finally {
-                        // Trick to call onDisconnected() in one place.
-                        if (shouldNotifyDisconnected) {
-                            if (DEBUG) {
-                                Log.d(TAG, "Notifying the controller of its disconnection");
-                            }
-                            try {
-                                caller.notifyDisconnected(0);
-                            } catch (RuntimeException e) {
-                                // Controller may be died prematurely.
-                                // Not an issue because we'll ignore it anyway.
-                            }
-                        }
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void close() {
-            mHandler.removeCallbacksAndMessages(null);
-            mService.clear();
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
deleted file mode 100644
index aff3204..0000000
--- a/apex/media/framework/java/android/media/MediaTranscodingManager.java
+++ /dev/null
@@ -1,1749 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.system.Os;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.annotation.MinSdk;
-import com.android.modules.utils.build.SdkLevel;
-
-import java.io.FileNotFoundException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- Android 12 introduces Compatible media transcoding feature.  See
- <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media
- transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
- AVC.
-
- <h3>Transcoding Types</h3>
- <h4>Video Transcoding</h4>
- When transcoding a video file, the video track will be transcoded based on the desired track format
- and the audio track will be pass through without any modification.
- <p class=note>
- Note that currently only support transcoding video file in mp4 format and with single video track.
-
- <h3>Transcoding Request</h3>
- <p>
- To transcode a media file, first create a {@link TranscodingRequest} through its builder class
- {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
- {@link MediaTranscodingManager#enqueueRequest(
-         TranscodingRequest, Executor, OnTranscodingFinishedListener)}
- TranscodeRequest are processed based on client process's priority and request priority. When a
- transcode operation is completed the caller is notified via its
- {@link OnTranscodingFinishedListener}.
- In the meantime the caller may use the returned TranscodingSession object to cancel or check the
- status of a specific transcode operation.
- <p>
- Here is an example where <code>Builder</code> is used to specify all parameters
-
- <pre class=prettyprint>
- VideoTranscodingRequest request =
-    new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build();
- }</pre>
- @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-@SystemApi
-public final class MediaTranscodingManager {
-    private static final String TAG = "MediaTranscodingManager";
-
-    /** Maximum number of retry to connect to the service. */
-    private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
-
-    /** Interval between trying to reconnect to the service. */
-    private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
-
-    /** Default bpp(bits-per-pixel) to use for calculating default bitrate. */
-    private static final float BPP = 0.25f;
-
-    /**
-     * Listener that gets notified when a transcoding operation has finished.
-     * This listener gets notified regardless of how the operation finished. It is up to the
-     * listener implementation to check the result and take appropriate action.
-     */
-    @FunctionalInterface
-    public interface OnTranscodingFinishedListener {
-        /**
-         * Called when the transcoding operation has finished. The receiver may use the
-         * TranscodingSession to check the result, i.e. whether the operation succeeded, was
-         * canceled or if an error occurred.
-         *
-         * @param session The TranscodingSession instance for the finished transcoding operation.
-         */
-        void onTranscodingFinished(@NonNull TranscodingSession session);
-    }
-
-    private final Context mContext;
-    private ContentResolver mContentResolver;
-    private final String mPackageName;
-    private final int mPid;
-    private final int mUid;
-    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
-    private final HashMap<Integer, TranscodingSession> mPendingTranscodingSessions = new HashMap();
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    @NonNull private ITranscodingClient mTranscodingClient = null;
-    private static MediaTranscodingManager sMediaTranscodingManager;
-
-    private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId and removes it from
-            // mPendingTranscodingSessions.
-            final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status and result.
-            session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                    TranscodingSession.RESULT_SUCCESS,
-                    TranscodingSession.ERROR_NONE);
-
-            // Notifies client the session is done.
-            if (session.mListener != null && session.mListenerExecutor != null) {
-                session.mListenerExecutor.execute(
-                        () -> session.mListener.onTranscodingFinished(session));
-            }
-        }
-    }
-
-    private void handleTranscodingFailed(int sessionId, int errorCode) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId and removes it from
-            // mPendingTranscodingSessions.
-            final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status and result.
-            session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                    TranscodingSession.RESULT_ERROR, errorCode);
-
-            // Notifies client the session failed.
-            if (session.mListener != null && session.mListenerExecutor != null) {
-                session.mListenerExecutor.execute(
-                        () -> session.mListener.onTranscodingFinished(session));
-            }
-        }
-    }
-
-    private void handleTranscodingProgressUpdate(int sessionId, int newProgress) {
-        synchronized (mPendingTranscodingSessions) {
-            // Gets the session associated with the sessionId.
-            final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session progress.
-            session.updateProgress(newProgress);
-
-            // Notifies client the progress update.
-            if (session.mProgressUpdateExecutor != null
-                    && session.mProgressUpdateListener != null) {
-                session.mProgressUpdateExecutor.execute(
-                        () -> session.mProgressUpdateListener.onProgressUpdate(session,
-                                newProgress));
-            }
-        }
-    }
-
-    private IMediaTranscodingService getService(boolean retry) {
-        // Do not try to get the service on pre-S. The service is lazy-start and getting the
-        // service could block.
-        if (!SdkLevel.isAtLeastS()) {
-            return null;
-        }
-
-        int retryCount = !retry ? 1 :  CONNECT_SERVICE_RETRY_COUNT;
-        Log.i(TAG, "get service with retry " + retryCount);
-        for (int count = 1;  count <= retryCount; count++) {
-            Log.d(TAG, "Trying to connect to service. Try count: " + count);
-            IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
-                    MediaFrameworkInitializer
-                    .getMediaServiceManager()
-                    .getMediaTranscodingServiceRegisterer()
-                    .get());
-            if (service != null) {
-                return service;
-            }
-            try {
-                // Sleep a bit before retry.
-                Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
-            } catch (InterruptedException ie) {
-                /* ignore */
-            }
-        }
-        Log.w(TAG, "Failed to get service");
-        return null;
-    }
-
-    /*
-     * Handle client binder died event.
-     * Upon receiving a binder died event of the client, we will do the following:
-     * 1) For the session that is running, notify the client that the session is failed with
-     *    error code,  so client could choose to retry the session or not.
-     *    TODO(hkuang): Add a new error code to signal service died error.
-     * 2) For the sessions that is still pending or paused, we will resubmit the session
-     *    once we successfully reconnect to the service and register a new client.
-     * 3) When trying to connect to the service and register a new client. The service may need time
-     *    to reboot or never boot up again. So we will retry for a number of times. If we still
-     *    could not connect, we will notify client session failure for the pending and paused
-     *    sessions.
-     */
-    private void onClientDied() {
-        synchronized (mLock) {
-            mTranscodingClient = null;
-        }
-
-        // Delegates the session notification and retry to the executor as it may take some time.
-        mExecutor.execute(() -> {
-            // List to track the sessions that we want to retry.
-            List<TranscodingSession> retrySessions = new ArrayList<TranscodingSession>();
-
-            // First notify the client of session failure for all the running sessions.
-            synchronized (mPendingTranscodingSessions) {
-                for (Map.Entry<Integer, TranscodingSession> entry :
-                        mPendingTranscodingSessions.entrySet()) {
-                    TranscodingSession session = entry.getValue();
-
-                    if (session.getStatus() == TranscodingSession.STATUS_RUNNING) {
-                        session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
-                                TranscodingSession.RESULT_ERROR,
-                                TranscodingSession.ERROR_SERVICE_DIED);
-
-                        // Remove the session from pending sessions.
-                        mPendingTranscodingSessions.remove(entry.getKey());
-
-                        if (session.mListener != null && session.mListenerExecutor != null) {
-                            Log.i(TAG, "Notify client session failed");
-                            session.mListenerExecutor.execute(
-                                    () -> session.mListener.onTranscodingFinished(session));
-                        }
-                    } else if (session.getStatus() == TranscodingSession.STATUS_PENDING
-                            || session.getStatus() == TranscodingSession.STATUS_PAUSED) {
-                        // Add the session to retrySessions to handle them later.
-                        retrySessions.add(session);
-                    }
-                }
-            }
-
-            // Try to register with the service once it boots up.
-            IMediaTranscodingService service = getService(true /*retry*/);
-            boolean haveTranscodingClient = false;
-            if (service != null) {
-                synchronized (mLock) {
-                    mTranscodingClient = registerClient(service);
-                    if (mTranscodingClient != null) {
-                        haveTranscodingClient = true;
-                    }
-                }
-            }
-
-            for (TranscodingSession session : retrySessions) {
-                // Notify the session failure if we fails to connect to the service or fail
-                // to retry the session.
-                if (!haveTranscodingClient) {
-                    // TODO(hkuang): Return correct error code to the client.
-                    handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
-                }
-
-                try {
-                    // Do not set hasRetried for retry initiated by MediaTranscodingManager.
-                    session.retryInternal(false /*setHasRetried*/);
-                } catch (Exception re) {
-                    // TODO(hkuang): Return correct error code to the client.
-                    handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
-                }
-            }
-        });
-    }
-
-    private void updateStatus(int sessionId, int status) {
-        synchronized (mPendingTranscodingSessions) {
-            final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
-
-            if (session == null) {
-                // This should not happen in reality.
-                Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
-                return;
-            }
-
-            // Updates the session status.
-            session.updateStatus(status);
-        }
-    }
-
-    // Just forwards all the events to the event handler.
-    private ITranscodingClientCallback mTranscodingClientCallback =
-            new ITranscodingClientCallback.Stub() {
-                // TODO(hkuang): Add more unit test to test difference file open mode.
-                @Override
-                public ParcelFileDescriptor openFileDescriptor(String fileUri, String mode)
-                        throws RemoteException {
-                    if (!mode.equals("r") && !mode.equals("w") && !mode.equals("rw")) {
-                        Log.e(TAG, "Unsupport mode: " + mode);
-                        return null;
-                    }
-
-                    Uri uri = Uri.parse(fileUri);
-                    try {
-                        AssetFileDescriptor afd = mContentResolver.openAssetFileDescriptor(uri,
-                                mode);
-                        if (afd != null) {
-                            return afd.getParcelFileDescriptor();
-                        }
-                    } catch (FileNotFoundException e) {
-                        Log.w(TAG, "Cannot find content uri: " + uri, e);
-                    } catch (SecurityException e) {
-                        Log.w(TAG, "Cannot open content uri: " + uri, e);
-                    } catch (Exception e) {
-                        Log.w(TAG, "Unknown content uri: " + uri, e);
-                    }
-                    return null;
-                }
-
-                @Override
-                public void onTranscodingStarted(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
-                }
-
-                @Override
-                public void onTranscodingPaused(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_PAUSED);
-                }
-
-                @Override
-                public void onTranscodingResumed(int sessionId) throws RemoteException {
-                    updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
-                }
-
-                @Override
-                public void onTranscodingFinished(int sessionId, TranscodingResultParcel result)
-                        throws RemoteException {
-                    handleTranscodingFinished(sessionId, result);
-                }
-
-                @Override
-                public void onTranscodingFailed(int sessionId, int errorCode)
-                        throws RemoteException {
-                    handleTranscodingFailed(sessionId, errorCode);
-                }
-
-                @Override
-                public void onAwaitNumberOfSessionsChanged(int sessionId, int oldAwaitNumber,
-                        int newAwaitNumber) throws RemoteException {
-                    //TODO(hkuang): Implement this.
-                }
-
-                @Override
-                public void onProgressUpdate(int sessionId, int newProgress)
-                        throws RemoteException {
-                    handleTranscodingProgressUpdate(sessionId, newProgress);
-                }
-            };
-
-    private ITranscodingClient registerClient(IMediaTranscodingService service) {
-        synchronized (mLock) {
-            try {
-                // Registers the client with MediaTranscoding service.
-                mTranscodingClient = service.registerClient(
-                        mTranscodingClientCallback,
-                        mPackageName,
-                        mPackageName);
-
-                if (mTranscodingClient != null) {
-                    mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0);
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to register new client due to exception " + ex);
-                mTranscodingClient = null;
-            }
-        }
-        return mTranscodingClient;
-    }
-
-    /**
-     * @hide
-     */
-    public MediaTranscodingManager(@NonNull Context context) {
-        mContext = context;
-        mContentResolver = mContext.getContentResolver();
-        mPackageName = mContext.getPackageName();
-        mUid = Os.getuid();
-        mPid = Os.getpid();
-    }
-
-    /**
-     * Abstract base class for all the TranscodingRequest.
-     * <p> TranscodingRequest encapsulates the desired configuration for the transcoding.
-     */
-    public abstract static class TranscodingRequest {
-        /**
-         *
-         * Default transcoding type.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_UNKNOWN = 0;
-
-        /**
-         * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video.
-         * <p>Note that currently only support transcoding video file in mp4 format.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_VIDEO = 1;
-
-        /**
-         * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image.
-         * @hide
-         */
-        public static final int TRANSCODING_TYPE_IMAGE = 2;
-
-        /** @hide */
-        @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
-                TRANSCODING_TYPE_UNKNOWN,
-                TRANSCODING_TYPE_VIDEO,
-                TRANSCODING_TYPE_IMAGE,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingType {}
-
-        /**
-         * Default value.
-         *
-         * @hide
-         */
-        public static final int PRIORITY_UNKNOWN = 0;
-        /**
-         * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
-         * client wants the transcoding result as soon as possible.
-         * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
-         * performance penalty due to resource reallocation to prioritize the sessions with higher
-         * priority.
-         *
-         * @hide
-         */
-        public static final int PRIORITY_REALTIME = 1;
-
-        /**
-         * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not
-         * need the transcoding result as soon as possible.
-         * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set
-         * to
-         * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
-         * delay of the transcoding result.
-         *
-         * @hide
-         *
-         */
-        public static final int PRIORITY_OFFLINE = 2;
-
-        /** @hide */
-        @IntDef(prefix = {"PRIORITY_"}, value = {
-                PRIORITY_UNKNOWN,
-                PRIORITY_REALTIME,
-                PRIORITY_OFFLINE,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingPriority {}
-
-        /** Uri of the source media file. */
-        private @NonNull Uri mSourceUri;
-
-        /** Uri of the destination media file. */
-        private @NonNull Uri mDestinationUri;
-
-        /** FileDescriptor of the source media file. */
-        private @Nullable ParcelFileDescriptor mSourceFileDescriptor;
-
-        /** FileDescriptor of the destination media file. */
-        private @Nullable ParcelFileDescriptor mDestinationFileDescriptor;
-
-        /**
-         *  The UID of the client that the TranscodingRequest is for. Only privileged caller could
-         *  set this Uid as only they could do the transcoding on behalf of the client.
-         *  -1 means not available.
-         */
-        private int mClientUid = -1;
-
-        /**
-         *  The Pid of the client that the TranscodingRequest is for. Only privileged caller could
-         *  set this Uid as only they could do the transcoding on behalf of the client.
-         *  -1 means not available.
-         */
-        private int mClientPid = -1;
-
-        /** Type of the transcoding. */
-        private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
-
-        /** Priority of the transcoding. */
-        private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
-
-        /**
-         * Desired image format for the destination file.
-         * <p> If this is null, source file's image track will be passed through and copied to the
-         * destination file.
-         * @hide
-         */
-        private @Nullable MediaFormat mImageFormat = null;
-
-        @VisibleForTesting
-        private TranscodingTestConfig mTestConfig = null;
-
-        /**
-         * Prevent public constructor access.
-         */
-        /* package private */ TranscodingRequest() {
-        }
-
-        private TranscodingRequest(Builder b) {
-            mSourceUri = b.mSourceUri;
-            mSourceFileDescriptor = b.mSourceFileDescriptor;
-            mDestinationUri = b.mDestinationUri;
-            mDestinationFileDescriptor = b.mDestinationFileDescriptor;
-            mClientUid = b.mClientUid;
-            mClientPid = b.mClientPid;
-            mPriority = b.mPriority;
-            mType = b.mType;
-            mTestConfig = b.mTestConfig;
-        }
-
-        /**
-         * Return the type of the transcoding.
-         * @hide
-         */
-        @TranscodingType
-        public int getType() {
-            return mType;
-        }
-
-        /** Return source uri of the transcoding. */
-        @NonNull
-        public Uri getSourceUri() {
-            return mSourceUri;
-        }
-
-        /**
-         * Return source file descriptor of the transcoding.
-         * This will be null if client has not provided it.
-         */
-        @Nullable
-        public ParcelFileDescriptor getSourceFileDescriptor() {
-            return mSourceFileDescriptor;
-        }
-
-        /** Return the UID of the client that this request is for. -1 means not available. */
-        public int getClientUid() {
-            return mClientUid;
-        }
-
-        /** Return the PID of the client that this request is for. -1 means not available. */
-        public int getClientPid() {
-            return mClientPid;
-        }
-
-        /** Return destination uri of the transcoding. */
-        @NonNull
-        public Uri getDestinationUri() {
-            return mDestinationUri;
-        }
-
-        /**
-         * Return destination file descriptor of the transcoding.
-         * This will be null if client has not provided it.
-         */
-        @Nullable
-        public ParcelFileDescriptor getDestinationFileDescriptor() {
-            return mDestinationFileDescriptor;
-        }
-
-        /**
-         * Return priority of the transcoding.
-         * @hide
-         */
-        @TranscodingPriority
-        public int getPriority() {
-            return mPriority;
-        }
-
-        /**
-         * Return TestConfig of the transcoding.
-         * @hide
-         */
-        @Nullable
-        public TranscodingTestConfig getTestConfig() {
-            return mTestConfig;
-        }
-
-        abstract void writeFormatToParcel(TranscodingRequestParcel parcel);
-
-        /* Writes the TranscodingRequest to a parcel. */
-        private TranscodingRequestParcel writeToParcel(@NonNull Context context) {
-            TranscodingRequestParcel parcel = new TranscodingRequestParcel();
-            switch (mPriority) {
-            case PRIORITY_OFFLINE:
-                parcel.priority = TranscodingSessionPriority.kUnspecified;
-                break;
-            case PRIORITY_REALTIME:
-            case PRIORITY_UNKNOWN:
-            default:
-                parcel.priority = TranscodingSessionPriority.kNormal;
-                break;
-            }
-            parcel.transcodingType = mType;
-            parcel.sourceFilePath = mSourceUri.toString();
-            parcel.sourceFd = mSourceFileDescriptor;
-            parcel.destinationFilePath = mDestinationUri.toString();
-            parcel.destinationFd = mDestinationFileDescriptor;
-            parcel.clientUid = mClientUid;
-            parcel.clientPid = mClientPid;
-            if (mClientUid < 0) {
-                parcel.clientPackageName = context.getPackageName();
-            } else {
-                String packageName = context.getPackageManager().getNameForUid(mClientUid);
-                // PackageName is optional as some uid does not have package name. Set to
-                // "Unavailable" string in this case.
-                if (packageName == null) {
-                    Log.w(TAG, "Failed to find package for uid: " + mClientUid);
-                    packageName = "Unavailable";
-                }
-                parcel.clientPackageName = packageName;
-            }
-            writeFormatToParcel(parcel);
-            if (mTestConfig != null) {
-                parcel.isForTesting = true;
-                parcel.testConfig = mTestConfig;
-            }
-            return parcel;
-        }
-
-        /**
-         * Builder to build a {@link TranscodingRequest} object.
-         *
-         * @param <T> The subclass to be built.
-         */
-        abstract static class Builder<T extends Builder<T>> {
-            private @NonNull Uri mSourceUri;
-            private @NonNull Uri mDestinationUri;
-            private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null;
-            private @Nullable ParcelFileDescriptor mDestinationFileDescriptor = null;
-            private int mClientUid = -1;
-            private int mClientPid = -1;
-            private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
-            private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
-            private TranscodingTestConfig mTestConfig;
-
-            abstract T self();
-
-            /**
-             * Creates a builder for building {@link TranscodingRequest}s.
-             *
-             * Client must set the source Uri. If client also provides the source fileDescriptor
-             * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)},
-             * TranscodingSession will use the fd instead of calling back to the client to open the
-             * sourceUri.
-             *
-             *
-             * @param type The transcoding type.
-             * @param sourceUri Content uri for the source media file.
-             * @param destinationUri Content uri for the destination media file.
-             *
-             */
-            private Builder(@TranscodingType int type, @NonNull Uri sourceUri,
-                    @NonNull Uri destinationUri) {
-                mType = type;
-
-                if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-empty source Uri.");
-                }
-                mSourceUri = sourceUri;
-
-                if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
-                    throw new IllegalArgumentException(
-                            "You must specify a non-empty destination Uri.");
-                }
-                mDestinationUri = destinationUri;
-            }
-
-            /**
-             * Specifies the fileDescriptor opened from the source media file.
-             *
-             * This call is optional. If the source fileDescriptor is provided, TranscodingSession
-             * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}.
-             * It is client's responsibility to make sure the fileDescriptor is opened from the
-             * source uri.
-             * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if fileDescriptor is invalid.
-             */
-            @NonNull
-            public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) {
-                if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
-                    throw new IllegalArgumentException(
-                            "Invalid source descriptor.");
-                }
-                mSourceFileDescriptor = fileDescriptor;
-                return self();
-            }
-
-            /**
-             * Specifies the fileDescriptor opened from the destination media file.
-             *
-             * This call is optional. If the destination fileDescriptor is provided,
-             * TranscodingSession will use it directly instead of opening the source uri from
-             * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's
-             * responsibility to make sure the fileDescriptor is opened from the destination uri.
-             * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media
-             *                       file.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if fileDescriptor is invalid.
-             */
-            @NonNull
-            public T setDestinationFileDescriptor(
-                    @NonNull ParcelFileDescriptor fileDescriptor) {
-                if (fileDescriptor == null || fileDescriptor.getFd() < 0) {
-                    throw new IllegalArgumentException(
-                            "Invalid destination descriptor.");
-                }
-                mDestinationFileDescriptor = fileDescriptor;
-                return self();
-            }
-
-            /**
-             * Specify the UID of the client that this request is for.
-             * <p>
-             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
-             * pid. Note that the permission check happens on the service side upon starting the
-             * transcoding. If the client does not have the permission, the transcoding will fail.
-             *
-             * @param uid client Uid.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if uid is invalid.
-             */
-            @NonNull
-            public T setClientUid(int uid) {
-                if (uid < 0) {
-                    throw new IllegalArgumentException("Invalid Uid");
-                }
-                mClientUid = uid;
-                return self();
-            }
-
-            /**
-             * Specify the pid of the client that this request is for.
-             * <p>
-             * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the
-             * pid. Note that the permission check happens on the service side upon starting the
-             * transcoding. If the client does not have the permission, the transcoding will fail.
-             *
-             * @param pid client Pid.
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if pid is invalid.
-             */
-            @NonNull
-            public T setClientPid(int pid) {
-                if (pid < 0) {
-                    throw new IllegalArgumentException("Invalid pid");
-                }
-                mClientPid = pid;
-                return self();
-            }
-
-            /**
-             * Specifies the priority of the transcoding.
-             *
-             * @param priority Must be one of the {@code PRIORITY_*}
-             * @return The same builder instance.
-             * @throws IllegalArgumentException if flags is invalid.
-             * @hide
-             */
-            @NonNull
-            public T setPriority(@TranscodingPriority int priority) {
-                if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
-                    throw new IllegalArgumentException("Invalid priority: " + priority);
-                }
-                mPriority = priority;
-                return self();
-            }
-
-            /**
-             * Sets the delay in processing this request.
-             * @param config test config.
-             * @return The same builder instance.
-             * @hide
-             */
-            @VisibleForTesting
-            @NonNull
-            public T setTestConfig(@NonNull TranscodingTestConfig config) {
-                mTestConfig = config;
-                return self();
-            }
-        }
-
-        /**
-         * Abstract base class for all the format resolvers.
-         */
-        abstract static class MediaFormatResolver {
-            private @NonNull ApplicationMediaCapabilities mClientCaps;
-
-            /**
-             * Prevents public constructor access.
-             */
-            /* package private */ MediaFormatResolver() {
-            }
-
-            /**
-             * Constructs MediaFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             */
-            MediaFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps) {
-                if (clientCaps == null) {
-                    throw new IllegalArgumentException("Client capabilities must not be null");
-                }
-                mClientCaps = clientCaps;
-            }
-
-            /**
-             * Returns the client capabilities.
-             */
-            @NonNull
-            /* package */ ApplicationMediaCapabilities getClientCapabilities() {
-                return mClientCaps;
-            }
-
-            abstract boolean shouldTranscode();
-        }
-
-        /**
-         * VideoFormatResolver for deciding if video transcoding is needed, and if so, the track
-         * formats to use.
-         */
-        public static class VideoFormatResolver extends MediaFormatResolver {
-            private static final int BIT_RATE = 20000000;            // 20Mbps
-
-            private MediaFormat mSrcVideoFormatHint;
-            private MediaFormat mSrcAudioFormatHint;
-
-            /**
-             * Constructs a new VideoFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             * @param srcVideoFormatHint A MediaFormat object containing information about the
-             *                           source's video track format that could affect the
-             *                           transcoding decision. Such information could include video
-             *                           codec types, color spaces, whether special format info (eg.
-             *                           slow-motion markers) are present, etc.. If a particular
-             *                           information is not present, it will not be used to make the
-             *                           decision.
-             */
-            public VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps,
-                    @NonNull MediaFormat srcVideoFormatHint) {
-                super(clientCaps);
-                mSrcVideoFormatHint = srcVideoFormatHint;
-            }
-
-            /**
-             * Constructs a new VideoFormatResolver object.
-             *
-             * @param clientCaps An ApplicationMediaCapabilities object containing the client's
-             *                   capabilities.
-             * @param srcVideoFormatHint A MediaFormat object containing information about the
-             *                           source's video track format that could affect the
-             *                           transcoding decision. Such information could include video
-             *                           codec types, color spaces, whether special format info (eg.
-             *                           slow-motion markers) are present, etc.. If a particular
-             *                           information is not present, it will not be used to make the
-             *                           decision.
-             * @param srcAudioFormatHint A MediaFormat object containing information about the
-             *                           source's audio track format that could affect the
-             *                           transcoding decision.
-             * @hide
-             */
-            VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps,
-                    @NonNull MediaFormat srcVideoFormatHint,
-                    @NonNull MediaFormat srcAudioFormatHint) {
-                super(clientCaps);
-                mSrcVideoFormatHint = srcVideoFormatHint;
-                mSrcAudioFormatHint = srcAudioFormatHint;
-            }
-
-            /**
-             * Returns whether the source content should be transcoded.
-             *
-             * @return true if the source should be transcoded.
-             */
-            public boolean shouldTranscode() {
-                boolean supportHevc = getClientCapabilities().isVideoMimeTypeSupported(
-                        MediaFormat.MIMETYPE_VIDEO_HEVC);
-                if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
-                        mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
-                    return true;
-                }
-                // TODO: add more checks as needed below.
-                return false;
-            }
-
-            /**
-             * Retrieves the video track format to be used on
-             * {@link VideoTranscodingRequest.Builder#setVideoTrackFormat(MediaFormat)} for this
-             * configuration.
-             *
-             * @return the video track format to be used if transcoding should be performed,
-             *         and null otherwise.
-             * @throws IllegalArgumentException if the hinted source video format contains invalid
-             *         parameters.
-             */
-            @Nullable
-            public MediaFormat resolveVideoFormat() {
-                if (!shouldTranscode()) {
-                    return null;
-                }
-
-                MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint);
-                videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
-
-                int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH, -1);
-                int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT, -1);
-                if (width <= 0 || height <= 0) {
-                    throw new IllegalArgumentException(
-                            "Source Width and height must be larger than 0");
-                }
-
-                float frameRate =
-                        mSrcVideoFormatHint.getNumber(MediaFormat.KEY_FRAME_RATE, 30.0)
-                        .floatValue();
-                if (frameRate <= 0) {
-                    throw new IllegalArgumentException(
-                            "frameRate must be larger than 0");
-                }
-
-                int bitrate = getAVCBitrate(width, height, frameRate);
-                videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
-                return videoTrackFormat;
-            }
-
-            /**
-             * Generate a default bitrate with the fixed bpp(bits-per-pixel) 0.25.
-             * This maps to:
-             * 1080P@30fps -> 16Mbps
-             * 1080P@60fps-> 32Mbps
-             * 4K@30fps -> 62Mbps
-             */
-            private static int getDefaultBitrate(int width, int height, float frameRate) {
-                return (int) (width * height * frameRate * BPP);
-            }
-
-            /**
-             * Query the bitrate from CamcorderProfile. If there are two profiles that match the
-             * width/height/framerate, we will use the higher one to get better quality.
-             * Return default bitrate if could not find any match profile.
-             */
-            private static int getAVCBitrate(int width, int height, float frameRate) {
-                int bitrate = -1;
-                int[] cameraIds = {0, 1};
-
-                // Profiles ordered in decreasing order of preference.
-                int[] preferQualities = {
-                        CamcorderProfile.QUALITY_2160P,
-                        CamcorderProfile.QUALITY_1080P,
-                        CamcorderProfile.QUALITY_720P,
-                        CamcorderProfile.QUALITY_480P,
-                        CamcorderProfile.QUALITY_LOW,
-                };
-
-                for (int cameraId : cameraIds) {
-                    for (int quality : preferQualities) {
-                        // Check if camera id has profile for the quality level.
-                        if (!CamcorderProfile.hasProfile(cameraId, quality)) {
-                            continue;
-                        }
-                        CamcorderProfile profile = CamcorderProfile.get(cameraId, quality);
-                        // Check the width/height/framerate/codec, also consider portrait case.
-                        if (((width == profile.videoFrameWidth
-                                && height == profile.videoFrameHeight)
-                                || (height == profile.videoFrameWidth
-                                && width == profile.videoFrameHeight))
-                                && (int) frameRate == profile.videoFrameRate
-                                && profile.videoCodec == MediaRecorder.VideoEncoder.H264) {
-                            if (bitrate < profile.videoBitRate) {
-                                bitrate = profile.videoBitRate;
-                            }
-                            break;
-                        }
-                    }
-                }
-
-                if (bitrate == -1) {
-                    Log.w(TAG, "Failed to find CamcorderProfile for w: " + width + "h: " + height
-                            + " fps: "
-                            + frameRate);
-                    bitrate = getDefaultBitrate(width, height, frameRate);
-                }
-                Log.d(TAG, "Using bitrate " + bitrate + " for " + width + " " + height + " "
-                        + frameRate);
-                return bitrate;
-            }
-
-            /**
-             * Retrieves the audio track format to be used for transcoding.
-             *
-             * @return the audio track format to be used if transcoding should be performed, and
-             *         null otherwise.
-             * @hide
-             */
-            @Nullable
-            public MediaFormat resolveAudioFormat() {
-                if (!shouldTranscode()) {
-                    return null;
-                }
-                // Audio transcoding is not supported yet, always return null.
-                return null;
-            }
-        }
-    }
-
-    /**
-     * VideoTranscodingRequest encapsulates the configuration for transcoding a video.
-     */
-    public static final class VideoTranscodingRequest extends TranscodingRequest {
-        /**
-         * Desired output video format of the destination file.
-         * <p> If this is null, source file's video track will be passed through and copied to the
-         * destination file.
-         */
-        private @Nullable MediaFormat mVideoTrackFormat = null;
-
-        /**
-         * Desired output audio format of the destination file.
-         * <p> If this is null, source file's audio track will be passed through and copied to the
-         * destination file.
-         */
-        private @Nullable MediaFormat mAudioTrackFormat = null;
-
-        private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) {
-            super(builder);
-            mVideoTrackFormat = builder.mVideoTrackFormat;
-            mAudioTrackFormat = builder.mAudioTrackFormat;
-        }
-
-        /**
-         * Return the video track format of the transcoding.
-         * This will be null if client has not specified the video track format.
-         */
-        @NonNull
-        public MediaFormat getVideoTrackFormat() {
-            return mVideoTrackFormat;
-        }
-
-        @Override
-        void writeFormatToParcel(TranscodingRequestParcel parcel) {
-            parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
-        }
-
-        /* Converts the MediaFormat to TranscodingVideoTrackFormat. */
-        private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) {
-            if (format == null) {
-                throw new IllegalArgumentException("Invalid MediaFormat");
-            }
-
-            TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat();
-
-            if (format.containsKey(MediaFormat.KEY_MIME)) {
-                String mime = format.getString(MediaFormat.KEY_MIME);
-                if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kAvc;
-                } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) {
-                    trackFormat.codecType = TranscodingVideoCodecType.kHevc;
-                } else {
-                    throw new UnsupportedOperationException("Only support transcode to avc/hevc");
-                }
-            }
-
-            if (format.containsKey(MediaFormat.KEY_BIT_RATE)) {
-                int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE);
-                if (bitrateBps <= 0) {
-                    throw new IllegalArgumentException("Bitrate must be larger than 0");
-                }
-                trackFormat.bitrateBps = bitrateBps;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey(
-                    MediaFormat.KEY_HEIGHT)) {
-                int width = format.getInteger(MediaFormat.KEY_WIDTH);
-                int height = format.getInteger(MediaFormat.KEY_HEIGHT);
-                if (width <= 0 || height <= 0) {
-                    throw new IllegalArgumentException("Width and height must be larger than 0");
-                }
-                // TODO: Validate the aspect ratio after adding scaling.
-                trackFormat.width = width;
-                trackFormat.height = height;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_PROFILE)) {
-                int profile = format.getInteger(MediaFormat.KEY_PROFILE);
-                if (profile <= 0) {
-                    throw new IllegalArgumentException("Invalid codec profile");
-                }
-                // TODO: Validate the profile according to codec type.
-                trackFormat.profile = profile;
-            }
-
-            if (format.containsKey(MediaFormat.KEY_LEVEL)) {
-                int level = format.getInteger(MediaFormat.KEY_LEVEL);
-                if (level <= 0) {
-                    throw new IllegalArgumentException("Invalid codec level");
-                }
-                // TODO: Validate the level according to codec type.
-                trackFormat.level = level;
-            }
-
-            return trackFormat;
-        }
-
-        /**
-         * Builder class for {@link VideoTranscodingRequest}.
-         */
-        public static final class Builder extends
-                TranscodingRequest.Builder<VideoTranscodingRequest.Builder> {
-            /**
-             * Desired output video format of the destination file.
-             * <p> If this is null, source file's video track will be passed through and
-             * copied to the destination file.
-             */
-            private @Nullable MediaFormat mVideoTrackFormat = null;
-
-            /**
-             * Desired output audio format of the destination file.
-             * <p> If this is null, source file's audio track will be passed through and copied
-             * to the destination file.
-             */
-            private @Nullable MediaFormat mAudioTrackFormat = null;
-
-            /**
-             * Creates a builder for building {@link VideoTranscodingRequest}s.
-             *
-             * <p> Client could only specify the settings that matters to them, e.g. codec format or
-             * bitrate. And by default, transcoding will preserve the original video's settings
-             * (bitrate, framerate, resolution) if not provided.
-             * <p>Note that some settings may silently fail to apply if the device does not support
-             * them.
-             * @param sourceUri Content uri for the source media file.
-             * @param destinationUri Content uri for the destination media file.
-             * @param videoFormat MediaFormat containing the settings that client wants override in
-             *                    the original video's video track.
-             * @throws IllegalArgumentException if videoFormat is invalid.
-             */
-            public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri,
-                    @NonNull MediaFormat videoFormat) {
-                super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri);
-                setVideoTrackFormat(videoFormat);
-            }
-
-            @Override
-            @NonNull
-            public Builder setClientUid(int uid) {
-                super.setClientUid(uid);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setClientPid(int pid) {
-                super.setClientPid(pid);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fd) {
-                super.setSourceFileDescriptor(fd);
-                return self();
-            }
-
-            @Override
-            @NonNull
-            public Builder setDestinationFileDescriptor(@NonNull ParcelFileDescriptor fd) {
-                super.setDestinationFileDescriptor(fd);
-                return self();
-            }
-
-            private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
-                if (videoFormat == null) {
-                    throw new IllegalArgumentException("videoFormat must not be null");
-                }
-
-                // Check if the MediaFormat is for video by looking at the MIME type.
-                String mime = videoFormat.containsKey(MediaFormat.KEY_MIME)
-                        ? videoFormat.getString(MediaFormat.KEY_MIME) : null;
-                if (mime == null || !mime.startsWith("video/")) {
-                    throw new IllegalArgumentException("Invalid video format: wrong mime type");
-                }
-
-                mVideoTrackFormat = videoFormat;
-            }
-
-            /**
-             * @return a new {@link TranscodingRequest} instance successfully initialized
-             * with all the parameters set on this <code>Builder</code>.
-             * @throws UnsupportedOperationException if the parameters set on the
-             *                                       <code>Builder</code> were incompatible, or
-             *                                       if they are not supported by the
-             *                                       device.
-             */
-            @NonNull
-            public VideoTranscodingRequest build() {
-                return new VideoTranscodingRequest(this);
-            }
-
-            @Override
-            VideoTranscodingRequest.Builder self() {
-                return this;
-            }
-        }
-    }
-
-    /**
-     * Handle to an enqueued transcoding operation. An instance of this class represents a single
-     * enqueued transcoding operation. The caller can use that instance to query the status or
-     * progress, and to get the result once the operation has completed.
-     */
-    public static final class TranscodingSession {
-        /** The session is enqueued but not yet running. */
-        public static final int STATUS_PENDING = 1;
-        /** The session is currently running. */
-        public static final int STATUS_RUNNING = 2;
-        /** The session is finished. */
-        public static final int STATUS_FINISHED = 3;
-        /** The session is paused. */
-        public static final int STATUS_PAUSED = 4;
-
-        /** @hide */
-        @IntDef(prefix = { "STATUS_" }, value = {
-                STATUS_PENDING,
-                STATUS_RUNNING,
-                STATUS_FINISHED,
-                STATUS_PAUSED,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Status {}
-
-        /** The session does not have a result yet. */
-        public static final int RESULT_NONE = 1;
-        /** The session completed successfully. */
-        public static final int RESULT_SUCCESS = 2;
-        /** The session encountered an error while running. */
-        public static final int RESULT_ERROR = 3;
-        /** The session was canceled by the caller. */
-        public static final int RESULT_CANCELED = 4;
-
-        /** @hide */
-        @IntDef(prefix = { "RESULT_" }, value = {
-                RESULT_NONE,
-                RESULT_SUCCESS,
-                RESULT_ERROR,
-                RESULT_CANCELED,
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Result {}
-
-
-        // The error code exposed here should be in sync with:
-        // frameworks/av/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl
-        /** @hide */
-        @IntDef(prefix = { "TRANSCODING_SESSION_ERROR_" }, value = {
-                ERROR_NONE,
-                ERROR_DROPPED_BY_SERVICE,
-                ERROR_SERVICE_DIED})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface TranscodingSessionErrorCode{}
-        /**
-         * Constant indicating that no error occurred.
-         */
-        public static final int ERROR_NONE = 0;
-
-        /**
-         * Constant indicating that the session is dropped by Transcoding service due to hitting
-         * the limit, e.g. too many back to back transcoding happen in a short time frame.
-         */
-        public static final int ERROR_DROPPED_BY_SERVICE = 1;
-
-        /**
-         * Constant indicating the backing transcoding service is died. Client should enqueue the
-         * the request again.
-         */
-        public static final int ERROR_SERVICE_DIED = 2;
-
-        /** Listener that gets notified when the progress changes. */
-        @FunctionalInterface
-        public interface OnProgressUpdateListener {
-            /**
-             * Called when the progress changes. The progress is in percentage between 0 and 1,
-             * where 0 means the session has not yet started and 100 means that it has finished.
-             *
-             * @param session      The session associated with the progress.
-             * @param progress The new progress ranging from 0 ~ 100 inclusive.
-             */
-            void onProgressUpdate(@NonNull TranscodingSession session,
-                    @IntRange(from = 0, to = 100) int progress);
-        }
-
-        private final MediaTranscodingManager mManager;
-        private Executor mListenerExecutor;
-        private OnTranscodingFinishedListener mListener;
-        private int mSessionId = -1;
-        // Lock for internal state.
-        private final Object mLock = new Object();
-        @GuardedBy("mLock")
-        private Executor mProgressUpdateExecutor = null;
-        @GuardedBy("mLock")
-        private OnProgressUpdateListener mProgressUpdateListener = null;
-        @GuardedBy("mLock")
-        private int mProgress = 0;
-        @GuardedBy("mLock")
-        private int mProgressUpdateInterval = 0;
-        @GuardedBy("mLock")
-        private @Status int mStatus = STATUS_PENDING;
-        @GuardedBy("mLock")
-        private @Result int mResult = RESULT_NONE;
-        @GuardedBy("mLock")
-        private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
-        @GuardedBy("mLock")
-        private boolean mHasRetried = false;
-        // The original request that associated with this session.
-        private final TranscodingRequest mRequest;
-
-        private TranscodingSession(
-                @NonNull MediaTranscodingManager manager,
-                @NonNull TranscodingRequest request,
-                @NonNull TranscodingSessionParcel parcel,
-                @NonNull @CallbackExecutor Executor executor,
-                @NonNull OnTranscodingFinishedListener listener) {
-            Objects.requireNonNull(manager, "manager must not be null");
-            Objects.requireNonNull(parcel, "parcel must not be null");
-            Objects.requireNonNull(executor, "listenerExecutor must not be null");
-            Objects.requireNonNull(listener, "listener must not be null");
-            mManager = manager;
-            mSessionId = parcel.sessionId;
-            mListenerExecutor = executor;
-            mListener = listener;
-            mRequest = request;
-        }
-
-        /**
-         * Set a progress listener.
-         * @param executor The executor on which listener will be invoked.
-         * @param listener The progress listener.
-         */
-        public void setOnProgressUpdateListener(
-                @NonNull @CallbackExecutor Executor executor,
-                @Nullable OnProgressUpdateListener listener) {
-            synchronized (mLock) {
-                Objects.requireNonNull(executor, "listenerExecutor must not be null");
-                Objects.requireNonNull(listener, "listener must not be null");
-                mProgressUpdateExecutor = executor;
-                mProgressUpdateListener = listener;
-            }
-        }
-
-        private void updateStatusAndResult(@Status int sessionStatus,
-                @Result int sessionResult, @TranscodingSessionErrorCode int errorCode) {
-            synchronized (mLock) {
-                mStatus = sessionStatus;
-                mResult = sessionResult;
-                mErrorCode = errorCode;
-            }
-        }
-
-        /**
-         * Retrieve the error code associated with the RESULT_ERROR.
-         */
-        public @TranscodingSessionErrorCode int getErrorCode() {
-            synchronized (mLock) {
-                return mErrorCode;
-            }
-        }
-
-        /**
-         * Resubmit the transcoding session to the service.
-         * Note that only the session that fails or gets cancelled could be retried and each session
-         * could be retried only once. After that, Client need to enqueue a new request if they want
-         * to try again.
-         *
-         * @return true if successfully resubmit the job to service. False otherwise.
-         * @throws UnsupportedOperationException if the retry could not be fulfilled.
-         * @hide
-         */
-        public boolean retry() {
-            return retryInternal(true /*setHasRetried*/);
-        }
-
-        // TODO(hkuang): Add more test for it.
-        private boolean retryInternal(boolean setHasRetried) {
-            synchronized (mLock) {
-                if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
-                    throw new UnsupportedOperationException(
-                            "Failed to retry as session is in processing");
-                }
-
-                if (mHasRetried) {
-                    throw new UnsupportedOperationException("Session has been retried already");
-                }
-
-                // Get the client interface.
-                ITranscodingClient client = mManager.getTranscodingClient();
-                if (client == null) {
-                    Log.e(TAG, "Service rebooting. Try again later");
-                    return false;
-                }
-
-                synchronized (mManager.mPendingTranscodingSessions) {
-                    try {
-                        // Submits the request to MediaTranscoding service.
-                        TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
-                        if (!client.submitRequest(mRequest.writeToParcel(mManager.mContext),
-                                                  sessionParcel)) {
-                            mHasRetried = true;
-                            throw new UnsupportedOperationException("Failed to enqueue request");
-                        }
-
-                        // Replace the old session id wit the new one.
-                        mSessionId = sessionParcel.sessionId;
-                        // Adds the new session back into pending sessions.
-                        mManager.mPendingTranscodingSessions.put(mSessionId, this);
-                    } catch (RemoteException re) {
-                        return false;
-                    }
-                    mStatus = STATUS_PENDING;
-                    mHasRetried = setHasRetried ? true : false;
-                }
-            }
-            return true;
-        }
-
-        /**
-         * Cancels the transcoding session and notify the listener.
-         * If the session happened to finish before being canceled this call is effectively a no-op
-         * and will not update the result in that case.
-         */
-        public void cancel() {
-            synchronized (mLock) {
-                // Check if the session is finished already.
-                if (mStatus != STATUS_FINISHED) {
-                    try {
-                        ITranscodingClient client = mManager.getTranscodingClient();
-                        // The client may be gone.
-                        if (client != null) {
-                            client.cancelSession(mSessionId);
-                        }
-                    } catch (RemoteException re) {
-                        //TODO(hkuang): Find out what to do if failing to cancel the session.
-                        Log.e(TAG, "Failed to cancel the session due to exception:  " + re);
-                    }
-                    mStatus = STATUS_FINISHED;
-                    mResult = RESULT_CANCELED;
-
-                    // Notifies client the session is canceled.
-                    mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
-                }
-            }
-        }
-
-        /**
-         * Gets the progress of the transcoding session. The progress is between 0 and 100, where 0
-         * means that the session has not yet started and 100 means that it is finished. For the
-         * cancelled session, the progress will be the last updated progress before it is cancelled.
-         * @return The progress.
-         */
-        @IntRange(from = 0, to = 100)
-        public int getProgress() {
-            synchronized (mLock) {
-                return mProgress;
-            }
-        }
-
-        /**
-         * Gets the status of the transcoding session.
-         * @return The status.
-         */
-        public @Status int getStatus() {
-            synchronized (mLock) {
-                return mStatus;
-            }
-        }
-
-        /**
-         * Adds a client uid that is also waiting for this transcoding session.
-         * <p>
-         * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
-         * uid. Note that the permission check happens on the service side upon starting the
-         * transcoding. If the client does not have the permission, the transcoding will fail.
-         * @param uid  the additional client uid to be added.
-         * @return true if successfully added, false otherwise.
-         */
-        public boolean addClientUid(int uid) {
-            if (uid < 0) {
-                throw new IllegalArgumentException("Invalid Uid");
-            }
-
-            // Get the client interface.
-            ITranscodingClient client = mManager.getTranscodingClient();
-            if (client == null) {
-                Log.e(TAG, "Service is dead...");
-                return false;
-            }
-
-            try {
-                if (!client.addClientUid(mSessionId, uid)) {
-                    Log.e(TAG, "Failed to add client uid");
-                    return false;
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to get client uids due to " + ex);
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * Query all the client that waiting for this transcoding session
-         * @return a list containing all the client uids.
-         */
-        @NonNull
-        public List<Integer> getClientUids() {
-            List<Integer> uidList = new ArrayList<Integer>();
-
-            // Get the client interface.
-            ITranscodingClient client = mManager.getTranscodingClient();
-            if (client == null) {
-                Log.e(TAG, "Service is dead...");
-                return uidList;
-            }
-
-            try {
-                int[] clientUids  = client.getClientUids(mSessionId);
-                for (int i : clientUids) {
-                    uidList.add(i);
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Failed to get client uids due to " + ex);
-            }
-
-            return uidList;
-        }
-
-        /**
-         * Gets sessionId of the transcoding session.
-         * @return session id.
-         */
-        public int getSessionId() {
-            return mSessionId;
-        }
-
-        /**
-         * Gets the result of the transcoding session.
-         * @return The result.
-         */
-        public @Result int getResult() {
-            synchronized (mLock) {
-                return mResult;
-            }
-        }
-
-        @Override
-        public String toString() {
-            String result;
-            String status;
-
-            switch (mResult) {
-                case RESULT_NONE:
-                    result = "RESULT_NONE";
-                    break;
-                case RESULT_SUCCESS:
-                    result = "RESULT_SUCCESS";
-                    break;
-                case RESULT_ERROR:
-                    result = "RESULT_ERROR(" + mErrorCode + ")";
-                    break;
-                case RESULT_CANCELED:
-                    result = "RESULT_CANCELED";
-                    break;
-                default:
-                    result = String.valueOf(mResult);
-                    break;
-            }
-
-            switch (mStatus) {
-                case STATUS_PENDING:
-                    status = "STATUS_PENDING";
-                    break;
-                case STATUS_PAUSED:
-                    status = "STATUS_PAUSED";
-                    break;
-                case STATUS_RUNNING:
-                    status = "STATUS_RUNNING";
-                    break;
-                case STATUS_FINISHED:
-                    status = "STATUS_FINISHED";
-                    break;
-                default:
-                    status = String.valueOf(mStatus);
-                    break;
-            }
-            return String.format(" session: {id: %d, status: %s, result: %s, progress: %d}",
-                    mSessionId, status, result, mProgress);
-        }
-
-        private void updateProgress(int newProgress) {
-            synchronized (mLock) {
-                mProgress = newProgress;
-            }
-        }
-
-        private void updateStatus(int newStatus) {
-            synchronized (mLock) {
-                mStatus = newStatus;
-            }
-        }
-    }
-
-    private ITranscodingClient getTranscodingClient() {
-        synchronized (mLock) {
-            return mTranscodingClient;
-        }
-    }
-
-    /**
-     * Enqueues a TranscodingRequest for execution.
-     * <p> Upon successfully accepting the request, MediaTranscodingManager will return a
-     * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to
-     * track the progress and get the result.
-     * <p> MediaTranscodingManager will return null if fails to accept the request due to service
-     * rebooting. Client could retry again after receiving null.
-     *
-     * @param transcodingRequest The TranscodingRequest to enqueue.
-     * @param listenerExecutor   Executor on which the listener is notified.
-     * @param listener           Listener to get notified when the transcoding session is finished.
-     * @return A TranscodingSession for this operation.
-     * @throws UnsupportedOperationException if the request could not be fulfilled.
-     */
-    @Nullable
-    public TranscodingSession enqueueRequest(
-            @NonNull TranscodingRequest transcodingRequest,
-            @NonNull @CallbackExecutor Executor listenerExecutor,
-            @NonNull OnTranscodingFinishedListener listener) {
-        Log.i(TAG, "enqueueRequest called.");
-        Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
-        Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
-        Objects.requireNonNull(listener, "listener must not be null");
-
-        // Converts the request to TranscodingRequestParcel.
-        TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(mContext);
-
-        Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri());
-
-        // Submits the request to MediaTranscoding service.
-        try {
-            TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
-            // Synchronizes the access to mPendingTranscodingSessions to make sure the session Id is
-            // inserted in the mPendingTranscodingSessions in the callback handler.
-            synchronized (mPendingTranscodingSessions) {
-                synchronized (mLock) {
-                    if (mTranscodingClient == null) {
-                        // Try to register with the service again.
-                        IMediaTranscodingService service = getService(false /*retry*/);
-                        if (service == null) {
-                            Log.w(TAG, "Service rebooting. Try again later");
-                            return null;
-                        }
-                        mTranscodingClient = registerClient(service);
-                        // If still fails, throws an exception to tell client to try later.
-                        if (mTranscodingClient == null) {
-                            Log.w(TAG, "Service rebooting. Try again later");
-                            return null;
-                        }
-                    }
-
-                    if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) {
-                        throw new UnsupportedOperationException("Failed to enqueue request");
-                    }
-                }
-
-                // Wraps the TranscodingSessionParcel into a TranscodingSession and returns it to
-                // client for tracking.
-                TranscodingSession session = new TranscodingSession(this, transcodingRequest,
-                        sessionParcel,
-                        listenerExecutor,
-                        listener);
-
-                // Adds the new session into pending sessions.
-                mPendingTranscodingSessions.put(session.getSessionId(), session);
-                return session;
-            }
-        } catch (RemoteException ex) {
-            Log.w(TAG, "Service rebooting. Try again later");
-            return null;
-        } catch (ServiceSpecificException ex) {
-            throw new UnsupportedOperationException(
-                    "Failed to submit request to Transcoding service. Error: " + ex);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
deleted file mode 100644
index 14d3ce8..0000000
--- a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.ParcelFileDescriptor;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * A DataSourceCallback that is backed by a ParcelFileDescriptor.
- */
-class ProxyDataSourceCallback extends DataSourceCallback {
-    private static final String TAG = "TestDataSourceCallback";
-
-    ParcelFileDescriptor mPFD;
-    FileDescriptor mFD;
-
-    ProxyDataSourceCallback(ParcelFileDescriptor pfd) throws IOException {
-        mPFD = pfd.dup();
-        mFD = mPFD.getFileDescriptor();
-    }
-
-    @Override
-    public synchronized int readAt(long position, byte[] buffer, int offset, int size)
-            throws IOException {
-        try {
-            Os.lseek(mFD, position, OsConstants.SEEK_SET);
-            int ret = Os.read(mFD, buffer, offset, size);
-            return (ret == 0) ? END_OF_STREAM : ret;
-        } catch (ErrnoException e) {
-            throw new IOException(e);
-        }
-    }
-
-    @Override
-    public synchronized long getSize() throws IOException {
-        return mPFD.getStatSize();
-    }
-
-    @Override
-    public synchronized void close() {
-        try {
-            mPFD.close();
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to close the PFD.", e);
-        }
-    }
-}
-
diff --git a/apex/media/framework/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java
deleted file mode 100644
index 2359813..0000000
--- a/apex/media/framework/java/android/media/RoutingDelegate.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.os.Handler;
-
-class RoutingDelegate implements AudioRouting.OnRoutingChangedListener {
-    private AudioRouting mAudioRouting;
-    private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
-    private Handler mHandler;
-
-    RoutingDelegate(final AudioRouting audioRouting,
-                    final AudioRouting.OnRoutingChangedListener listener,
-                    Handler handler) {
-        mAudioRouting = audioRouting;
-        mOnRoutingChangedListener = listener;
-        mHandler = handler;
-    }
-
-    public AudioRouting.OnRoutingChangedListener getListener() {
-        return mOnRoutingChangedListener;
-    }
-
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    @Override
-    public void onRoutingChanged(AudioRouting router) {
-        if (mOnRoutingChangedListener != null) {
-            mOnRoutingChangedListener.onRoutingChanged(mAudioRouting);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
deleted file mode 100644
index 7e71591..0000000
--- a/apex/media/framework/java/android/media/Session2Command.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.Objects;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
- * <p>
- * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
- * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
- * {@link #getCustomAction()} shouldn't be {@code null}.
- * <p>
- * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html">
- * AndroidX SessionCommand</a> class for the list of valid commands.
- */
-public final class Session2Command implements Parcelable {
-    /**
-     * Command code for the custom command which can be defined by string action in the
-     * {@link Session2Command}.
-     */
-    public static final int COMMAND_CODE_CUSTOM = 0;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2Command> CREATOR =
-            new Parcelable.Creator<Session2Command>() {
-                @Override
-                public Session2Command createFromParcel(Parcel in) {
-                    return new Session2Command(in);
-                }
-
-                @Override
-                public Session2Command[] newArray(int size) {
-                    return new Session2Command[size];
-                }
-            };
-
-    private final int mCommandCode;
-    // Nonnull if it's custom command
-    private final String mCustomAction;
-    private final Bundle mCustomExtras;
-
-    /**
-     * Constructor for creating a command predefined in AndroidX media2.
-     *
-     * @param commandCode A command code for a command predefined in AndroidX media2.
-     */
-    public Session2Command(int commandCode) {
-        if (commandCode == COMMAND_CODE_CUSTOM) {
-            throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
-        }
-        mCommandCode = commandCode;
-        mCustomAction = null;
-        mCustomExtras = null;
-    }
-
-    /**
-     * Constructor for creating a custom command.
-     *
-     * @param action The action of this custom command.
-     * @param extras An extra bundle for this custom command.
-     */
-    public Session2Command(@NonNull String action, @Nullable Bundle extras) {
-        if (action == null) {
-            throw new IllegalArgumentException("action shouldn't be null");
-        }
-        mCommandCode = COMMAND_CODE_CUSTOM;
-        mCustomAction = action;
-        mCustomExtras = extras;
-    }
-
-    /**
-     * Used by parcelable creator.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    Session2Command(Parcel in) {
-        mCommandCode = in.readInt();
-        mCustomAction = in.readString();
-        mCustomExtras = in.readBundle();
-    }
-
-    /**
-     * Gets the command code of a predefined command.
-     * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
-     */
-    public int getCommandCode() {
-        return mCommandCode;
-    }
-
-    /**
-     * Gets the action of a custom command.
-     * This will return {@code null} for a predefined command.
-     */
-    @Nullable
-    public String getCustomAction() {
-        return mCustomAction;
-    }
-
-    /**
-     * Gets the extra bundle of a custom command.
-     * This will return {@code null} for a predefined command.
-     */
-    @Nullable
-    public Bundle getCustomExtras() {
-        return mCustomExtras;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        if (dest == null) {
-            throw new IllegalArgumentException("parcel shouldn't be null");
-        }
-        dest.writeInt(mCommandCode);
-        dest.writeString(mCustomAction);
-        dest.writeBundle(mCustomExtras);
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (!(obj instanceof Session2Command)) {
-            return false;
-        }
-        Session2Command other = (Session2Command) obj;
-        return mCommandCode == other.mCommandCode
-                && TextUtils.equals(mCustomAction, other.mCustomAction);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mCustomAction, mCommandCode);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Contains the result of {@link Session2Command}.
-     */
-    public static final class Result {
-        private final int mResultCode;
-        private final Bundle mResultData;
-
-        /**
-         * Result code representing that the command is skipped or canceled. For an example, a seek
-         * command can be skipped if it is followed by another seek command.
-         */
-        public static final int RESULT_INFO_SKIPPED = 1;
-
-        /**
-         * Result code representing that the command is successfully completed.
-         */
-        public static final int RESULT_SUCCESS = 0;
-
-        /**
-         * Result code represents that call is ended with an unknown error.
-         */
-        public static final int RESULT_ERROR_UNKNOWN_ERROR = -1;
-
-        /**
-         * Constructor of {@link Result}.
-         *
-         * @param resultCode result code
-         * @param resultData result data
-         */
-        public Result(int resultCode, @Nullable Bundle resultData) {
-            mResultCode = resultCode;
-            mResultData = resultData;
-        }
-
-        /**
-         * Returns the result code.
-         */
-        public int getResultCode() {
-            return mResultCode;
-        }
-
-        /**
-         * Returns the result data.
-         */
-        @Nullable
-        public Bundle getResultData() {
-            return mResultData;
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
deleted file mode 100644
index af8184a..0000000
--- a/apex/media/framework/java/android/media/Session2CommandGroup.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * A set of {@link Session2Command} which represents a command group.
- */
-public final class Session2CommandGroup implements Parcelable {
-    private static final String TAG = "Session2CommandGroup";
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2CommandGroup>
-            CREATOR = new Parcelable.Creator<Session2CommandGroup>() {
-                @Override
-                public Session2CommandGroup createFromParcel(Parcel in) {
-                    return new Session2CommandGroup(in);
-                }
-
-                @Override
-                public Session2CommandGroup[] newArray(int size) {
-                    return new Session2CommandGroup[size];
-                }
-            };
-
-    Set<Session2Command> mCommands = new HashSet<>();
-
-    /**
-     * Creates a new Session2CommandGroup with commands copied from another object.
-     *
-     * @param commands The collection of commands to copy.
-     */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
-        if (commands != null) {
-            mCommands.addAll(commands);
-        }
-    }
-
-    /**
-     * Used by parcelable creator.
-     */
-    @SuppressWarnings({"WeakerAccess", "UnsafeParcelApi"}) /* synthetic access */
-    Session2CommandGroup(Parcel in) {
-        Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader());
-        if (commands != null) {
-            for (Parcelable command : commands) {
-                mCommands.add((Session2Command) command);
-            }
-        }
-    }
-
-    /**
-     * Checks whether this command group has a command that matches given {@code command}.
-     *
-     * @param command A command to find. Shouldn't be {@code null}.
-     */
-    public boolean hasCommand(@NonNull Session2Command command) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        return mCommands.contains(command);
-    }
-
-    /**
-     * Checks whether this command group has a command that matches given {@code commandCode}.
-     *
-     * @param commandCode A command code to find.
-     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
-     */
-    public boolean hasCommand(int commandCode) {
-        if (commandCode == COMMAND_CODE_CUSTOM) {
-            throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
-        }
-        for (Session2Command command : mCommands) {
-            if (command.getCommandCode() == commandCode) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Gets all commands of this command group.
-     */
-    @NonNull
-    public Set<Session2Command> getCommands() {
-        return new HashSet<>(mCommands);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        if (dest == null) {
-            throw new IllegalArgumentException("parcel shouldn't be null");
-        }
-        dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0);
-    }
-
-    /**
-     * This API is not generally intended for third party application developers.
-     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
-     * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
-     * Library</a> for consistent behavior across all devices.
-     * <p>
-     * Builds a {@link Session2CommandGroup} object.
-     */
-    public static final class Builder {
-        private Set<Session2Command> mCommands;
-
-        public Builder() {
-            mCommands = new HashSet<>();
-        }
-
-        /**
-         * Creates a new builder for {@link Session2CommandGroup} with commands copied from another
-         * {@link Session2CommandGroup} object.
-         * @param commandGroup
-         */
-        public Builder(@NonNull Session2CommandGroup commandGroup) {
-            if (commandGroup == null) {
-                throw new IllegalArgumentException("command group shouldn't be null");
-            }
-            mCommands = commandGroup.getCommands();
-        }
-
-        /**
-         * Adds a command to this command group.
-         *
-         * @param command A command to add. Shouldn't be {@code null}.
-         */
-        @NonNull
-        public Builder addCommand(@NonNull Session2Command command) {
-            if (command == null) {
-                throw new IllegalArgumentException("command shouldn't be null");
-            }
-            mCommands.add(command);
-            return this;
-        }
-
-        /**
-         * Removes a command from this group which matches given {@code command}.
-         *
-         * @param command A command to find. Shouldn't be {@code null}.
-         */
-        @NonNull
-        public Builder removeCommand(@NonNull Session2Command command) {
-            if (command == null) {
-                throw new IllegalArgumentException("command shouldn't be null");
-            }
-            mCommands.remove(command);
-            return this;
-        }
-
-        /**
-         * Builds {@link Session2CommandGroup}.
-         *
-         * @return a new {@link Session2CommandGroup}.
-         */
-        @NonNull
-        public Session2CommandGroup build() {
-            return new Session2CommandGroup(mCommands);
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java
deleted file mode 100644
index 6e550e8..0000000
--- a/apex/media/framework/java/android/media/Session2Link.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.NonNull;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-import java.util.Objects;
-
-/**
- * Handles incoming commands from {@link MediaController2} to {@link MediaSession2}.
- * @hide
- */
-// @SystemApi
-public final class Session2Link implements Parcelable {
-    private static final String TAG = "Session2Link";
-    private static final boolean DEBUG = MediaSession2.DEBUG;
-
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2Link> CREATOR =
-            new Parcelable.Creator<Session2Link>() {
-                @Override
-                public Session2Link createFromParcel(Parcel in) {
-                    return new Session2Link(in);
-                }
-
-                @Override
-                public Session2Link[] newArray(int size) {
-                    return new Session2Link[size];
-                }
-            };
-
-    private final MediaSession2 mSession;
-    private final IMediaSession2 mISession;
-
-    public Session2Link(MediaSession2 session) {
-        mSession = session;
-        mISession = new Session2Stub();
-    }
-
-    Session2Link(Parcel in) {
-        mSession = null;
-        mISession = IMediaSession2.Stub.asInterface(in.readStrongBinder());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mISession.asBinder());
-    }
-
-    @Override
-    public int hashCode() {
-        return mISession.asBinder().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Session2Link)) {
-            return false;
-        }
-        Session2Link other = (Session2Link) obj;
-        return Objects.equals(mISession.asBinder(), other.mISession.asBinder());
-    }
-
-    /** Link to death with mISession */
-    public void linkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
-        if (mISession != null) {
-            try {
-                mISession.asBinder().linkToDeath(recipient, flags);
-            } catch (RemoteException e) {
-                if (DEBUG) {
-                    Log.d(TAG, "Session died too early.", e);
-                }
-            }
-        }
-    }
-
-    /** Unlink to death with mISession */
-    public boolean unlinkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) {
-        if (mISession != null) {
-            return mISession.asBinder().unlinkToDeath(recipient, flags);
-        }
-        return true;
-    }
-
-    /** Interface method for IMediaSession2.connect */
-    public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-        try {
-            mISession.connect(caller, seq, connectionRequest);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.disconnect */
-    public void disconnect(final Controller2Link caller, int seq) {
-        try {
-            mISession.disconnect(caller, seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.sendSessionCommand */
-    public void sendSessionCommand(final Controller2Link caller, final int seq,
-            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-        try {
-            mISession.sendSessionCommand(caller, seq, command, args, resultReceiver);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Interface method for IMediaSession2.sendSessionCommand */
-    public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-        try {
-            mISession.cancelSessionCommand(caller, seq);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /** Stub implementation for IMediaSession2.connect */
-    public void onConnect(final Controller2Link caller, int pid, int uid, int seq,
-            Bundle connectionRequest) {
-        mSession.onConnect(caller, pid, uid, seq, connectionRequest);
-    }
-
-    /** Stub implementation for IMediaSession2.disconnect */
-    public void onDisconnect(final Controller2Link caller, int seq) {
-        mSession.onDisconnect(caller, seq);
-    }
-
-    /** Stub implementation for IMediaSession2.sendSessionCommand */
-    public void onSessionCommand(final Controller2Link caller, final int seq,
-            final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-        mSession.onSessionCommand(caller, seq, command, args, resultReceiver);
-    }
-
-    /** Stub implementation for IMediaSession2.cancelSessionCommand */
-    public void onCancelCommand(final Controller2Link caller, final int seq) {
-        mSession.onCancelCommand(caller, seq);
-    }
-
-    private class Session2Stub extends IMediaSession2.Stub {
-        @Override
-        public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) {
-            if (caller == null || connectionRequest == null) {
-                return;
-            }
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void disconnect(final Controller2Link caller, int seq) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onDisconnect(caller, seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void sendSessionCommand(final Controller2Link caller, final int seq,
-                final Session2Command command, final Bundle args, ResultReceiver resultReceiver) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void cancelSessionCommand(final Controller2Link caller, final int seq) {
-            if (caller == null) {
-                return;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Session2Link.this.onCancelCommand(caller, seq);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    }
-}
diff --git a/apex/media/framework/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java
deleted file mode 100644
index aae2e1b..0000000
--- a/apex/media/framework/java/android/media/Session2Token.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
- * Library</a> for consistent behavior across all devices.
- * <p>
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
- * This may be passed to apps by the session owner to allow them to create a
- * {@link MediaController2} to communicate with the session.
- * <p>
- * It can be also obtained by {@link android.media.session.MediaSessionManager}.
- */
-public final class Session2Token implements Parcelable {
-    private static final String TAG = "Session2Token";
-
-    public static final @android.annotation.NonNull Creator<Session2Token> CREATOR =
-            new Creator<Session2Token>() {
-                @Override
-                public Session2Token createFromParcel(Parcel p) {
-                    return new Session2Token(p);
-                }
-
-                @Override
-                public Session2Token[] newArray(int size) {
-                    return new Session2Token[size];
-                }
-            };
-
-    /**
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
-    public @interface TokenType {
-    }
-
-    /**
-     * Type for {@link MediaSession2}.
-     */
-    public static final int TYPE_SESSION = 0;
-
-    /**
-     * Type for {@link MediaSession2Service}.
-     */
-    public static final int TYPE_SESSION_SERVICE = 1;
-
-    private final int mUid;
-    @TokenType
-    private final int mType;
-    private final String mPackageName;
-    private final String mServiceName;
-    private final Session2Link mSessionLink;
-    private final ComponentName mComponentName;
-    private final Bundle mExtras;
-
-    /**
-     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}.
-     *
-     * @param context The context.
-     * @param serviceComponent The component name of the service.
-     */
-    public Session2Token(@NonNull Context context, @NonNull ComponentName serviceComponent) {
-        if (context == null) {
-            throw new IllegalArgumentException("context shouldn't be null");
-        }
-        if (serviceComponent == null) {
-            throw new IllegalArgumentException("serviceComponent shouldn't be null");
-        }
-
-        final PackageManager manager = context.getPackageManager();
-        final int uid = getUid(manager, serviceComponent.getPackageName());
-
-        if (!isInterfaceDeclared(manager, MediaSession2Service.SERVICE_INTERFACE,
-                serviceComponent)) {
-            Log.w(TAG, serviceComponent + " doesn't implement MediaSession2Service.");
-        }
-        mComponentName = serviceComponent;
-        mPackageName = serviceComponent.getPackageName();
-        mServiceName = serviceComponent.getClassName();
-        mUid = uid;
-        mType = TYPE_SESSION_SERVICE;
-        mSessionLink = null;
-        mExtras = Bundle.EMPTY;
-    }
-
-    Session2Token(int uid, int type, String packageName, Session2Link sessionLink,
-            @NonNull Bundle tokenExtras) {
-        mUid = uid;
-        mType = type;
-        mPackageName = packageName;
-        mServiceName = null;
-        mComponentName = null;
-        mSessionLink = sessionLink;
-        mExtras = tokenExtras;
-    }
-
-    Session2Token(Parcel in) {
-        mUid = in.readInt();
-        mType = in.readInt();
-        mPackageName = in.readString();
-        mServiceName = in.readString();
-        mSessionLink = in.readParcelable(null);
-        mComponentName = ComponentName.unflattenFromString(in.readString());
-
-        Bundle extras = in.readBundle();
-        if (extras == null) {
-            Log.w(TAG, "extras shouldn't be null.");
-            extras = Bundle.EMPTY;
-        } else if (MediaSession2.hasCustomParcelable(extras)) {
-            Log.w(TAG, "extras contain custom parcelable. Ignoring.");
-            extras = Bundle.EMPTY;
-        }
-        mExtras = extras;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mUid);
-        dest.writeInt(mType);
-        dest.writeString(mPackageName);
-        dest.writeString(mServiceName);
-        dest.writeParcelable(mSessionLink, flags);
-        dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString());
-        dest.writeBundle(mExtras);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof Session2Token)) {
-            return false;
-        }
-        Session2Token other = (Session2Token) obj;
-        return mUid == other.mUid
-                && TextUtils.equals(mPackageName, other.mPackageName)
-                && TextUtils.equals(mServiceName, other.mServiceName)
-                && mType == other.mType
-                && Objects.equals(mSessionLink, other.mSessionLink);
-    }
-
-    @Override
-    public String toString() {
-        return "Session2Token {pkg=" + mPackageName + " type=" + mType
-                + " service=" + mServiceName + " Session2Link=" + mSessionLink + "}";
-    }
-
-    /**
-     * @return uid of the session
-     */
-    public int getUid() {
-        return mUid;
-    }
-
-    /**
-     * @return package name of the session
-     */
-    @NonNull
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    /**
-     * @return service name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
-     */
-    @Nullable
-    public String getServiceName() {
-        return mServiceName;
-    }
-
-    /**
-     * @return type of the token
-     * @see #TYPE_SESSION
-     * @see #TYPE_SESSION_SERVICE
-     */
-    public @TokenType int getType() {
-        return mType;
-    }
-
-    /**
-     * @return extras of the token
-     * @see MediaSession2.Builder#setExtras(Bundle)
-     */
-    @NonNull
-    public Bundle getExtras() {
-        return new Bundle(mExtras);
-    }
-
-    Session2Link getSessionLink() {
-        return mSessionLink;
-    }
-
-    private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface,
-            ComponentName serviceComponent) {
-        Intent serviceIntent = new Intent(serviceInterface);
-        // Use queryIntentServices to find services with MediaSession2Service.SERVICE_INTERFACE.
-        // We cannot use resolveService with intent specified class name, because resolveService
-        // ignores actions if Intent.setClassName() is specified.
-        serviceIntent.setPackage(serviceComponent.getPackageName());
-
-        List<ResolveInfo> list = manager.queryIntentServices(
-                serviceIntent, PackageManager.GET_META_DATA);
-        if (list != null) {
-            for (int i = 0; i < list.size(); i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-                    continue;
-                }
-                if (TextUtils.equals(
-                        resolveInfo.serviceInfo.name, serviceComponent.getClassName())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static int getUid(PackageManager manager, String packageName) {
-        try {
-            return manager.getApplicationInfo(packageName, 0).uid;
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("Cannot find package " + packageName);
-        }
-    }
-}
diff --git a/apex/media/framework/jni/android_media_MediaParserJNI.cpp b/apex/media/framework/jni/android_media_MediaParserJNI.cpp
deleted file mode 100644
index c81152c..0000000
--- a/apex/media/framework/jni/android_media_MediaParserJNI.cpp
+++ /dev/null
@@ -1,96 +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.
- */
-
-#include <jni.h>
-#include <media/MediaMetrics.h>
-
-#define JNI_FUNCTION(RETURN_TYPE, NAME, ...)                                               \
-    extern "C" {                                                                           \
-    JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
-                                                                ##__VA_ARGS__);            \
-    }                                                                                      \
-    JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
-                                                                ##__VA_ARGS__)
-
-namespace {
-
-constexpr char kMediaMetricsKey[] = "mediaparser";
-
-constexpr char kAttributeLogSessionId[] = "android.media.mediaparser.logSessionId";
-constexpr char kAttributeParserName[] = "android.media.mediaparser.parserName";
-constexpr char kAttributeCreatedByName[] = "android.media.mediaparser.createdByName";
-constexpr char kAttributeParserPool[] = "android.media.mediaparser.parserPool";
-constexpr char kAttributeLastException[] = "android.media.mediaparser.lastException";
-constexpr char kAttributeResourceByteCount[] = "android.media.mediaparser.resourceByteCount";
-constexpr char kAttributeDurationMillis[] = "android.media.mediaparser.durationMillis";
-constexpr char kAttributeTrackMimeTypes[] = "android.media.mediaparser.trackMimeTypes";
-constexpr char kAttributeTrackCodecs[] = "android.media.mediaparser.trackCodecs";
-constexpr char kAttributeAlteredParameters[] = "android.media.mediaparser.alteredParameters";
-constexpr char kAttributeVideoWidth[] = "android.media.mediaparser.videoWidth";
-constexpr char kAttributeVideoHeight[] = "android.media.mediaparser.videoHeight";
-
-// Util class to handle string resource management.
-class JstringHandle {
-public:
-    JstringHandle(JNIEnv* env, jstring value) : mEnv(env), mJstringValue(value) {
-        mCstringValue = env->GetStringUTFChars(value, /* isCopy= */ nullptr);
-    }
-
-    ~JstringHandle() {
-        if (mCstringValue != nullptr) {
-            mEnv->ReleaseStringUTFChars(mJstringValue, mCstringValue);
-        }
-    }
-
-    [[nodiscard]] const char* value() const {
-        return mCstringValue != nullptr ? mCstringValue : "";
-    }
-
-    JNIEnv* mEnv;
-    jstring mJstringValue;
-    const char* mCstringValue;
-};
-
-} // namespace
-
-JNI_FUNCTION(void, nativeSubmitMetrics, jstring logSessionIdJstring, jstring parserNameJstring,
-             jboolean createdByName, jstring parserPoolJstring, jstring lastExceptionJstring,
-             jlong resourceByteCount, jlong durationMillis, jstring trackMimeTypesJstring,
-             jstring trackCodecsJstring, jstring alteredParameters, jint videoWidth,
-             jint videoHeight) {
-    mediametrics_handle_t item(mediametrics_create(kMediaMetricsKey));
-    mediametrics_setCString(item, kAttributeLogSessionId,
-                            JstringHandle(env, logSessionIdJstring).value());
-    mediametrics_setCString(item, kAttributeParserName,
-                            JstringHandle(env, parserNameJstring).value());
-    mediametrics_setInt32(item, kAttributeCreatedByName, createdByName ? 1 : 0);
-    mediametrics_setCString(item, kAttributeParserPool,
-                            JstringHandle(env, parserPoolJstring).value());
-    mediametrics_setCString(item, kAttributeLastException,
-                            JstringHandle(env, lastExceptionJstring).value());
-    mediametrics_setInt64(item, kAttributeResourceByteCount, resourceByteCount);
-    mediametrics_setInt64(item, kAttributeDurationMillis, durationMillis);
-    mediametrics_setCString(item, kAttributeTrackMimeTypes,
-                            JstringHandle(env, trackMimeTypesJstring).value());
-    mediametrics_setCString(item, kAttributeTrackCodecs,
-                            JstringHandle(env, trackCodecsJstring).value());
-    mediametrics_setCString(item, kAttributeAlteredParameters,
-                            JstringHandle(env, alteredParameters).value());
-    mediametrics_setInt32(item, kAttributeVideoWidth, videoWidth);
-    mediametrics_setInt32(item, kAttributeVideoHeight, videoHeight);
-    mediametrics_selfRecord(item);
-    mediametrics_delete(item);
-}
diff --git a/apex/media/framework/lint-baseline.xml b/apex/media/framework/lint-baseline.xml
deleted file mode 100644
index 95eea45..0000000
--- a/apex/media/framework/lint-baseline.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
-        errorLine1="        if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {"
-        errorLine2="                                                        ~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java"
-            line="121"
-            column="57"/>
-    </issue>
-
-    <issue
-        id="DefaultLocale"
-        message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
-        errorLine1="            return String.format(&quot; session: {id: %d, status: %s, result: %s, progress: %d}&quot;,"
-        errorLine2="                   ^">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/MediaTranscodingManager.java"
-            line="1651"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
-        errorLine1="            Bundle out = parcel.readBundle(null);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/MediaSession2.java"
-            line="303"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
-        errorLine1="        mCustomExtras = in.readBundle();"
-        errorLine2="                           ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Command.java"
-            line="104"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Passing null here (to use the default class loader) will not work if you are restoring your own classes. Consider using for example `getClass().getClassLoader()` instead."
-        errorLine1="        mSessionLink = in.readParcelable(null);"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
-            line="141"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="ParcelClassLoader"
-        message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
-        errorLine1="        Bundle extras = in.readBundle();"
-        errorLine2="                           ~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/apex/media/framework/java/android/media/Session2Token.java"
-            line="144"
-            column="28"/>
-    </issue>
-
-</issues>
diff --git a/apex/media/framework/updatable-media-proguard.flags b/apex/media/framework/updatable-media-proguard.flags
deleted file mode 100644
index 4e7d842..0000000
--- a/apex/media/framework/updatable-media-proguard.flags
+++ /dev/null
@@ -1,2 +0,0 @@
-# Keep all symbols in android.media.
--keep class android.media.* {*;}
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
deleted file mode 100644
index 834e5cb..0000000
--- a/apex/media/service/Android.bp
+++ /dev/null
@@ -1,56 +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 {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-filegroup {
-    name: "service-media-s-sources",
-    srcs: [
-        "java/**/*.java",
-    ],
-    path: "java",
-    visibility: ["//visibility:private"],
-}
-
-java_sdk_library {
-    name: "service-media-s",
-    permitted_packages: [
-        "com.android.server.media",
-    ],
-    defaults: ["framework-system-server-module-defaults"],
-    srcs: [
-        ":service-media-s-sources",
-    ],
-    libs: [
-        "androidx.annotation_annotation",
-        "updatable-media",
-        "modules-annotation-minsdk",
-        "modules-utils-build",
-    ],
-    jarjar_rules: "jarjar_rules.txt",
-    sdk_version: "system_server_current",
-    min_sdk_version: "29", // TODO: We may need to bump this at some point.
-    lint: {
-        strict_updatability_linting: true,
-    },
-    apex_available: [
-        "com.android.media",
-    ],
-}
diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/system-server-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/media/service/api/system-server-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/media/service/jarjar_rules.txt b/apex/media/service/jarjar_rules.txt
deleted file mode 100644
index 7e37c2b..0000000
--- a/apex/media/service/jarjar_rules.txt
+++ /dev/null
@@ -1 +0,0 @@
-rule com.android.modules.** android.media.internal.@1
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
deleted file mode 100644
index 4223fa6..0000000
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ /dev/null
@@ -1,685 +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 com.android.server.media;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.os.UserHandle.ALL;
-import static android.os.UserHandle.getUserHandleForUid;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.media.IMediaCommunicationService;
-import android.media.IMediaCommunicationServiceCallback;
-import android.media.MediaController2;
-import android.media.MediaParceledListSlice;
-import android.media.Session2CommandGroup;
-import android.media.Session2Token;
-import android.media.session.MediaSessionManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.KeyEvent;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.server.SystemService;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-/**
- * A system service that manages {@link android.media.MediaSession2} creations
- * and their ongoing media playback state.
- * @hide
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-public class MediaCommunicationService extends SystemService {
-    private static final String TAG = "MediaCommunicationSrv";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    final Context mContext;
-
-    final Object mLock = new Object();
-    final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    @GuardedBy("mLock")
-    private final SparseIntArray mFullUserIds = new SparseIntArray();
-    @GuardedBy("mLock")
-    private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
-
-    final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
-    @GuardedBy("mLock")
-    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<>();
-    final NotificationManager mNotificationManager;
-    MediaSessionManager mSessionManager;
-
-    public MediaCommunicationService(Context context) {
-        super(context);
-        mContext = context;
-        mNotificationManager = context.getSystemService(NotificationManager.class);
-    }
-
-    @Override
-    public void onStart() {
-        publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub());
-        updateUser();
-    }
-
-    @Override
-    public void onBootPhase(int phase) {
-        super.onBootPhase(phase);
-        switch (phase) {
-            // This ensures MediaSessionService is started
-            case PHASE_BOOT_COMPLETED:
-                mSessionManager = mContext.getSystemService(MediaSessionManager.class);
-                break;
-        }
-    }
-
-    @Override
-    public void onUserStarting(@NonNull TargetUser user) {
-        if (DEBUG) Log.d(TAG, "onUserStarting: " + user);
-        updateUser();
-    }
-
-    @Override
-    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-        if (DEBUG) Log.d(TAG, "onUserSwitching: " + to);
-        updateUser();
-    }
-
-    @Override
-    public void onUserStopped(@NonNull TargetUser targetUser) {
-        int userId = targetUser.getUserHandle().getIdentifier();
-
-        if (DEBUG) Log.d(TAG, "onUserStopped: " + userId);
-        synchronized (mLock) {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                if (user.getFullUserId() == userId) {
-                    user.destroyAllSessions();
-                    mUserRecords.remove(userId);
-                } else {
-                    user.destroySessionsForUser(userId);
-                }
-            }
-        }
-        updateUser();
-    }
-
-    @Nullable
-    CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) {
-        if (callback == null) {
-            return null;
-        }
-        for (CallbackRecord record : mCallbackRecords) {
-            if (Objects.equals(callback.asBinder(), record.mCallback.asBinder())) {
-                return record;
-            }
-        }
-        return null;
-    }
-
-    ArrayList<Session2Token> getSession2TokensLocked(int userId) {
-        ArrayList<Session2Token> list = new ArrayList<>();
-        if (userId == ALL.getIdentifier()) {
-            int size = mUserRecords.size();
-            for (int i = 0; i < size; i++) {
-                list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
-            }
-        } else {
-            FullUserRecord user = getFullUserRecordLocked(userId);
-            if (user != null) {
-                list.addAll(user.getSession2Tokens(userId));
-            }
-        }
-        return list;
-    }
-
-    private FullUserRecord getFullUserRecordLocked(int userId) {
-        int fullUserId = mFullUserIds.get(userId, -1);
-        if (fullUserId < 0) {
-            return null;
-        }
-        return mUserRecords.get(fullUserId);
-    }
-
-    private boolean hasMediaControlPermission(int pid, int uid) {
-        // Check if it's system server or has MEDIA_CONTENT_CONTROL.
-        // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
-        // check here.
-        if (uid == Process.SYSTEM_UID || mContext.checkPermission(
-                android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
-                == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        } else if (DEBUG) {
-            Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
-        }
-        return false;
-    }
-
-    private void updateUser() {
-        UserManager manager = mContext.getSystemService(UserManager.class);
-        List<UserHandle> allUsers = manager.getUserHandles(/*excludeDying=*/false);
-
-        synchronized (mLock) {
-            mFullUserIds.clear();
-            if (allUsers != null) {
-                for (UserHandle user : allUsers) {
-                    UserHandle parent = manager.getProfileParent(user);
-                    if (parent != null) {
-                        mFullUserIds.put(user.getIdentifier(), parent.getIdentifier());
-                    } else {
-                        mFullUserIds.put(user.getIdentifier(), user.getIdentifier());
-                        if (mUserRecords.get(user.getIdentifier()) == null) {
-                            mUserRecords.put(user.getIdentifier(),
-                                    new FullUserRecord(user.getIdentifier()));
-                        }
-                    }
-                }
-            }
-            // Ensure that the current full user exists.
-            int currentFullUserId = ActivityManager.getCurrentUser();
-            FullUserRecord currentFullUserRecord = mUserRecords.get(currentFullUserId);
-            if (currentFullUserRecord == null) {
-                Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId);
-                currentFullUserRecord = new FullUserRecord(currentFullUserId);
-                mUserRecords.put(currentFullUserId, currentFullUserRecord);
-            }
-            mFullUserIds.put(currentFullUserId, currentFullUserId);
-        }
-    }
-
-    void dispatchSession2Created(Session2Token token) {
-        synchronized (mLock) {
-            for (CallbackRecord record : mCallbackRecords) {
-                if (record.mUserId != ALL.getIdentifier()
-                        && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
-                    continue;
-                }
-                try {
-                    record.mCallback.onSession2Created(token);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to notify session2 token created " + record);
-                }
-            }
-        }
-    }
-
-    void dispatchSession2Changed(int userId) {
-        ArrayList<Session2Token> allSession2Tokens;
-        ArrayList<Session2Token> userSession2Tokens;
-
-        synchronized (mLock) {
-            allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier());
-            userSession2Tokens = getSession2TokensLocked(userId);
-
-            for (CallbackRecord record : mCallbackRecords) {
-                if (record.mUserId == ALL.getIdentifier()) {
-                    try {
-                        MediaParceledListSlice<Session2Token> toSend =
-                                new MediaParceledListSlice<>(allSession2Tokens);
-                        toSend.setInlineCountLimit(0);
-                        record.mCallback.onSession2Changed(toSend);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
-                    }
-                } else if (record.mUserId == userId) {
-                    try {
-                        MediaParceledListSlice<Session2Token> toSend =
-                                new MediaParceledListSlice<>(userSession2Tokens);
-                        toSend.setInlineCountLimit(0);
-                        record.mCallback.onSession2Changed(toSend);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify session2 tokens changed " + record);
-                    }
-                }
-            }
-        }
-    }
-
-    void onSessionDied(Session2Record session) {
-        if (DEBUG) {
-            Log.d(TAG, "Destroying " + session);
-        }
-        if (session.isClosed()) {
-            Log.w(TAG, "Destroying already destroyed session. Ignoring.");
-            return;
-        }
-
-        FullUserRecord user = session.getFullUser();
-        if (user != null) {
-            user.removeSession(session);
-        }
-        session.close();
-    }
-
-    void onSessionPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-        FullUserRecord user = session.getFullUser();
-        if (user == null || !user.containsSession(session)) {
-            Log.d(TAG, "Unknown session changed playback state. Ignoring.");
-            return;
-        }
-        user.onPlaybackStateChanged(session, promotePriority);
-    }
-
-
-    static boolean isMediaSessionKey(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                return true;
-        }
-        return false;
-    }
-
-    private class Stub extends IMediaCommunicationService.Stub {
-        @Override
-        public void notifySession2Created(Session2Token sessionToken) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Session2 is created " + sessionToken);
-                }
-                if (uid != sessionToken.getUid()) {
-                    throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
-                            + " but actually=" + sessionToken.getUid());
-                }
-                FullUserRecord user;
-                int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
-                synchronized (mLock) {
-                    user = getFullUserRecordLocked(userId);
-                }
-                if (user == null) {
-                    Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
-                    return;
-                }
-                user.addSession(new Session2Record(MediaCommunicationService.this,
-                        user, sessionToken, mRecordExecutor));
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL
-         * permission or an enabled notification listener)
-         *
-         * @param controllerPackageName package name of the controller app
-         * @param controllerPid pid of the controller app
-         * @param controllerUid uid of the controller app
-         */
-        @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid,
-                int controllerUid) {
-            final int uid = Binder.getCallingUid();
-            final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Don't perform check between controllerPackageName and controllerUid.
-                // When an (activity|service) runs on the another apps process by specifying
-                // android:process in the AndroidManifest.xml, then PID and UID would have the
-                // running process' information instead of the (activity|service) that has created
-                // MediaController.
-                // Note that we can use Context#getOpPackageName() instead of
-                // Context#getPackageName() for getting package name that matches with the PID/UID,
-                // but it doesn't tell which package has created the MediaController, so useless.
-                return hasMediaControlPermission(controllerPid, controllerUid)
-                        || hasEnabledNotificationListener(
-                        userId, controllerPackageName, controllerUid);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public MediaParceledListSlice getSession2Tokens(int userId) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-
-            try {
-                // Check that they can make calls on behalf of the user and get the final user id
-                int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
-                ArrayList<Session2Token> result;
-                synchronized (mLock) {
-                    result = getSession2TokensLocked(resolvedUserId);
-                }
-                MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
-                parceledListSlice.setInlineCountLimit(1);
-                return parceledListSlice;
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void dispatchMediaKeyEvent(String packageName, KeyEvent keyEvent,
-                boolean asSystemService) {
-            if (keyEvent == null || !isMediaSessionKey(keyEvent.getKeyCode())) {
-                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
-                return;
-            }
-
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
-            final long token = Binder.clearCallingIdentity();
-            try {
-                //TODO: Dispatch key event to media session 2 if required
-                mSessionManager.dispatchMediaKeyEvent(keyEvent, asSystemService);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void registerCallback(IMediaCommunicationServiceCallback callback,
-                String packageName) throws RemoteException {
-            Objects.requireNonNull(callback, "callback should not be null");
-            Objects.requireNonNull(packageName, "packageName should not be null");
-
-            synchronized (mLock) {
-                if (findCallbackRecordLocked(callback) == null) {
-
-                    CallbackRecord record = new CallbackRecord(callback, packageName,
-                            Binder.getCallingUid(), Binder.getCallingPid());
-                    mCallbackRecords.add(record);
-                    try {
-                        callback.asBinder().linkToDeath(record, 0);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to register callback", e);
-                        mCallbackRecords.remove(record);
-                    }
-                } else {
-                    Log.e(TAG, "registerCallback is called with already registered callback. "
-                            + "packageName=" + packageName);
-                }
-            }
-        }
-
-        @Override
-        public void unregisterCallback(IMediaCommunicationServiceCallback callback)
-                throws RemoteException {
-            synchronized (mLock) {
-                CallbackRecord existingRecord = findCallbackRecordLocked(callback);
-                if (existingRecord != null) {
-                    mCallbackRecords.remove(existingRecord);
-                    callback.asBinder().unlinkToDeath(existingRecord, 0);
-                } else {
-                    Log.e(TAG, "unregisterCallback is called with unregistered callback.");
-                }
-            }
-        }
-
-        private boolean hasEnabledNotificationListener(int callingUserId,
-                String controllerPackageName, int controllerUid) {
-            int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier();
-            if (callingUserId != controllerUserId) {
-                // Enabled notification listener only works within the same user.
-                return false;
-            }
-
-            if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName,
-                    UserHandle.getUserHandleForUid(controllerUid))) {
-                return true;
-            }
-            if (DEBUG) {
-                Log.d(TAG, controllerPackageName + " (uid=" + controllerUid
-                        + ") doesn't have an enabled notification listener");
-            }
-            return false;
-        }
-
-        // Handles incoming user by checking whether the caller has permission to access the
-        // given user id's information or not. Permission is not necessary if the given user id is
-        // equal to the caller's user id, but if not, the caller needs to have the
-        // INTERACT_ACROSS_USERS_FULL permission. Otherwise, a security exception will be thrown.
-        // The return value will be the given user id, unless the given user id is
-        // UserHandle.CURRENT, which will return the ActivityManager.getCurrentUser() value instead.
-        private int handleIncomingUser(int pid, int uid, int userId, String packageName) {
-            int callingUserId = UserHandle.getUserHandleForUid(uid).getIdentifier();
-            if (userId == callingUserId) {
-                return userId;
-            }
-
-            boolean canInteractAcrossUsersFull = mContext.checkPermission(
-                    INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED;
-            if (canInteractAcrossUsersFull) {
-                if (userId == UserHandle.CURRENT.getIdentifier()) {
-                    return ActivityManager.getCurrentUser();
-                }
-                return userId;
-            }
-
-            throw new SecurityException("Permission denied while calling from " + packageName
-                    + " with user id: " + userId + "; Need to run as either the calling user id ("
-                    + callingUserId + "), or with " + INTERACT_ACROSS_USERS_FULL + " permission");
-        }
-    }
-
-    final class CallbackRecord implements IBinder.DeathRecipient {
-        private final IMediaCommunicationServiceCallback mCallback;
-        private final String mPackageName;
-        private final int mUid;
-        private int mPid;
-        private final int mUserId;
-
-        CallbackRecord(IMediaCommunicationServiceCallback callback,
-                String packageName, int uid, int pid) {
-            mCallback = callback;
-            mPackageName = packageName;
-            mUid = uid;
-            mPid = pid;
-            mUserId = (mContext.checkPermission(
-                    INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED)
-                    ? ALL.getIdentifier() : UserHandle.getUserHandleForUid(mUid).getIdentifier();
-        }
-
-        @Override
-        public String toString() {
-            return "CallbackRecord[callback=" + mCallback + ", pkg=" + mPackageName
-                    + ", uid=" + mUid + ", pid=" + mPid + "]";
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mCallbackRecords.remove(this);
-            }
-        }
-    }
-
-    final class FullUserRecord {
-        private final int mFullUserId;
-        private final SessionPriorityList mSessionPriorityList = new SessionPriorityList();
-
-        FullUserRecord(int fullUserId) {
-            mFullUserId = fullUserId;
-        }
-
-        public void addSession(Session2Record record) {
-            mSessionPriorityList.addSession(record);
-            mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-        }
-
-        private void removeSession(Session2Record record) {
-            mSessionPriorityList.removeSession(record);
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-            //TODO: Handle if the removed session was the media button session.
-        }
-
-        public int getFullUserId() {
-            return mFullUserId;
-        }
-
-        public List<Session2Token> getAllSession2Tokens() {
-            return mSessionPriorityList.getAllTokens();
-        }
-
-        public List<Session2Token> getSession2Tokens(int userId) {
-            return mSessionPriorityList.getTokensByUserId(userId);
-        }
-
-        public void destroyAllSessions() {
-            mSessionPriorityList.destroyAllSessions();
-            mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-        }
-
-        public void destroySessionsForUser(int userId) {
-            if (mSessionPriorityList.destroySessionsByUserId(userId)) {
-                mHandler.post(() -> dispatchSession2Changed(mFullUserId));
-            }
-        }
-
-        public boolean containsSession(Session2Record session) {
-            return mSessionPriorityList.contains(session);
-        }
-
-        public void onPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-            mSessionPriorityList.onPlaybackStateChanged(session, promotePriority);
-        }
-    }
-
-    static final class Session2Record {
-        final Session2Token mSessionToken;
-        final Object mSession2RecordLock = new Object();
-        final WeakReference<MediaCommunicationService> mServiceRef;
-        final WeakReference<FullUserRecord> mFullUserRef;
-        @GuardedBy("mSession2RecordLock")
-        private final MediaController2 mController;
-
-        @GuardedBy("mSession2RecordLock")
-        boolean mIsConnected;
-        @GuardedBy("mSession2RecordLock")
-        private boolean mIsClosed;
-
-        //TODO: introduce policy (See MediaSessionPolicyProvider)
-        Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
-                Session2Token token, Executor controllerExecutor) {
-            mServiceRef = new WeakReference<>(service);
-            mFullUserRef = new WeakReference<>(fullUser);
-            mSessionToken = token;
-            mController = new MediaController2.Builder(service.getContext(), token)
-                    .setControllerCallback(controllerExecutor, new Controller2Callback())
-                    .build();
-        }
-
-        public int getUserId() {
-            return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
-        }
-
-        public FullUserRecord getFullUser() {
-            return mFullUserRef.get();
-        }
-
-        public boolean isClosed() {
-            synchronized (mSession2RecordLock) {
-                return mIsClosed;
-            }
-        }
-
-        public void close() {
-            synchronized (mSession2RecordLock) {
-                mIsClosed = true;
-                mController.close();
-            }
-        }
-
-        public Session2Token getSessionToken() {
-            return mSessionToken;
-        }
-
-        public boolean checkPlaybackActiveState(boolean expected) {
-            synchronized (mSession2RecordLock) {
-                return mIsConnected && mController.isPlaybackActive() == expected;
-            }
-        }
-
-        private class Controller2Callback extends MediaController2.ControllerCallback {
-            @Override
-            public void onConnected(MediaController2 controller,
-                    Session2CommandGroup allowedCommands) {
-                if (DEBUG) {
-                    Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
-                }
-                synchronized (mSession2RecordLock) {
-                    mIsConnected = true;
-                }
-            }
-
-            @Override
-            public void onDisconnected(MediaController2 controller) {
-                if (DEBUG) {
-                    Log.d(TAG, "disconnected from " + mSessionToken);
-                }
-                synchronized (mSession2RecordLock) {
-                    mIsConnected = false;
-                }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    service.onSessionDied(Session2Record.this);
-                }
-            }
-
-            @Override
-            public void onPlaybackActiveChanged(
-                    @NonNull MediaController2 controller,
-                    boolean playbackActive) {
-                if (DEBUG) {
-                    Log.d(TAG, "playback active changed, " + mSessionToken + ", active="
-                            + playbackActive);
-                }
-                MediaCommunicationService service = mServiceRef.get();
-                if (service != null) {
-                    service.onSessionPlaybackStateChanged(Session2Record.this, playbackActive);
-                }
-            }
-        }
-    }
-}
diff --git a/apex/media/service/java/com/android/server/media/SessionPriorityList.java b/apex/media/service/java/com/android/server/media/SessionPriorityList.java
deleted file mode 100644
index 8145861..0000000
--- a/apex/media/service/java/com/android/server/media/SessionPriorityList.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright 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.media;
-
-import android.annotation.Nullable;
-import android.media.Session2Token;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.modules.annotation.MinSdk;
-import com.android.server.media.MediaCommunicationService.Session2Record;
-
-import java.util.ArrayList;
-import java.util.List;
-
-//TODO: Define the priority specifically.
-/**
- * Keeps track of media sessions and their priority for notifications, media
- * button dispatch, etc.
- * Higher priority session has more chance to be selected as media button session,
- * which receives the media button events.
- */
-@MinSdk(Build.VERSION_CODES.S)
-@RequiresApi(Build.VERSION_CODES.S)
-class SessionPriorityList {
-    private static final String TAG = "SessionPriorityList";
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private final List<Session2Record> mSessions = new ArrayList<>();
-
-    @Nullable
-    private Session2Record mMediaButtonSession;
-    @Nullable
-    private Session2Record mCachedVolumeSession;
-
-    //TODO: integrate AudioPlayerStateMonitor
-
-    public void addSession(Session2Record record) {
-        synchronized (mLock) {
-            mSessions.add(record);
-        }
-    }
-
-    public void removeSession(Session2Record record) {
-        synchronized (mLock) {
-            mSessions.remove(record);
-        }
-        if (record == mMediaButtonSession) {
-            updateMediaButtonSession(null);
-        }
-    }
-
-    public void destroyAllSessions() {
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                session.close();
-            }
-            mSessions.clear();
-        }
-    }
-
-    public boolean destroySessionsByUserId(int userId) {
-        boolean changed = false;
-        synchronized (mLock) {
-            for (int i = mSessions.size() - 1; i >= 0; i--) {
-                Session2Record session = mSessions.get(i);
-                if (session.getUserId() == userId) {
-                    mSessions.remove(i);
-                    session.close();
-                    changed = true;
-                }
-            }
-        }
-        return changed;
-    }
-
-    public List<Session2Token> getAllTokens() {
-        List<Session2Token> sessions = new ArrayList<>();
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                sessions.add(session.getSessionToken());
-            }
-        }
-        return sessions;
-    }
-
-    public List<Session2Token> getTokensByUserId(int userId) {
-        List<Session2Token> sessions = new ArrayList<>();
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                if (session.getUserId() == userId) {
-                    sessions.add(session.getSessionToken());
-                }
-            }
-        }
-        return sessions;
-    }
-
-    /** Gets the media button session which receives the media button events. */
-    @Nullable
-    public Session2Record getMediaButtonSession() {
-        return mMediaButtonSession;
-    }
-
-    /** Gets the media volume session which receives the volume key events. */
-    @Nullable
-    public Session2Record getMediaVolumeSession() {
-        //TODO: if null, calculate it.
-        return mCachedVolumeSession;
-    }
-
-    public boolean contains(Session2Record session) {
-        synchronized (mLock) {
-            return mSessions.contains(session);
-        }
-    }
-
-    public void onPlaybackStateChanged(Session2Record session, boolean promotePriority) {
-        if (promotePriority) {
-            synchronized (mLock) {
-                if (mSessions.remove(session)) {
-                    mSessions.add(0, session);
-                } else {
-                    Log.w(TAG, "onPlaybackStateChanged: Ignoring unknown session");
-                    return;
-                }
-            }
-        } else if (session.checkPlaybackActiveState(false)) {
-            // Just clear the cached volume session when a state goes inactive
-            mCachedVolumeSession = null;
-        }
-
-        // In most cases, playback state isn't needed for finding the media button session,
-        // but we only use it as a hint if an app has multiple local media sessions.
-        // In that case, we pick the media session whose PlaybackState matches
-        // the audio playback configuration.
-        if (mMediaButtonSession != null
-                && mMediaButtonSession.getSessionToken().getUid()
-                == session.getSessionToken().getUid()) {
-            Session2Record newMediaButtonSession =
-                    findMediaButtonSession(mMediaButtonSession.getSessionToken().getUid());
-            if (newMediaButtonSession != mMediaButtonSession) {
-                // Check if the policy states that this session should not be updated as a media
-                // button session.
-                updateMediaButtonSession(newMediaButtonSession);
-            }
-        }
-    }
-
-    private void updateMediaButtonSession(@Nullable Session2Record newSession) {
-        mMediaButtonSession = newSession;
-        //TODO: invoke callbacks for media button session changed listeners
-    }
-
-    /**
-     * Finds the media button session with the given {@param uid}.
-     * If the app has multiple media sessions, the media session whose playback state is not null
-     * and matches the audio playback state becomes the media button session. Otherwise the top
-     * priority session becomes the media button session.
-     *
-     * @return The media button session. Returns {@code null} if the app doesn't have a media
-     *   session.
-     */
-    @Nullable
-    private Session2Record findMediaButtonSession(int uid) {
-        Session2Record mediaButtonSession = null;
-        synchronized (mLock) {
-            for (Session2Record session : mSessions) {
-                if (uid != session.getSessionToken().getUid()) {
-                    continue;
-                }
-                // TODO: check audio player state monitor
-                if (mediaButtonSession == null) {
-                    // Pick the top priority session as a default.
-                    mediaButtonSession = session;
-                }
-            }
-        }
-        return mediaButtonSession;
-    }
-}
diff --git a/apex/media/service/lint-baseline.xml b/apex/media/service/lint-baseline.xml
deleted file mode 100644
index def6baf..0000000
--- a/apex/media/service/lint-baseline.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.2.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.2.0-dev">
-
-</issues>
diff --git a/api/Android.bp b/api/Android.bp
index e902530..bbe26b7 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -93,6 +93,7 @@
 // Silence reflection warnings. See b/168689341
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
 metalava_cmd += " --quiet --no-banner --format=v2 "
+metalava_cmd += " --hide ChangedThrows "
 
 genrule {
     name: "current-api-xml",
@@ -112,7 +113,7 @@
         "framework-appsearch",
         "framework-bluetooth",
         "framework-connectivity",
-        "framework-connectivity-tiramisu",
+        "framework-connectivity-t",
         "framework-graphics",
         "framework-media",
         "framework-mediaprovider",
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9564dde..c5410a0 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -189,6 +189,8 @@
                 instrument.abi = nextArgRequired();
             } else if (opt.equals("--no-restart")) {
                 instrument.noRestart = true;
+            } else if (opt.equals("--always-check-signature")) {
+                instrument.alwaysCheckSignature = true;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 0b439df..a0562d9 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -16,6 +16,7 @@
 
 package com.android.commands.am;
 
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -95,6 +96,7 @@
     public Bundle args = new Bundle();
     // Required
     public String componentNameArg;
+    public boolean alwaysCheckSignature = false;
 
     /**
      * Construct the instrument command runner.
@@ -519,6 +521,9 @@
             if (noRestart) {
                 flags |= INSTR_FLAG_NO_RESTART;
             }
+            if (alwaysCheckSignature) {
+                flags |= INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
+            }
             if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
                         abi)) {
                 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index c134822..2694c7b 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,6 +23,7 @@
 
 cc_defaults {
     name: "idmap2_defaults",
+    cpp_std: "gnu++2b",
     tidy: true,
     tidy_checks: [
         "modernize-*",
@@ -31,6 +32,7 @@
         "android-*",
         "misc-*",
         "readability-*",
+        "-readability-identifier-length",
     ],
     tidy_checks_as_errors: [
         "modernize-*",
@@ -39,6 +41,7 @@
         "-modernize-pass-by-value",
         "-modernize-replace-disallow-copy-and-assign-macro",
         "-modernize-return-braced-init-list",
+        "-modernize-use-default-member-init",
         "-modernize-use-equals-default",
         "-modernize-use-nodiscard",
         "-modernize-use-override",
@@ -52,7 +55,6 @@
         "-readability-const-return-type",
         "-readability-convert-member-functions-to-static",
         "-readability-else-after-return",
-        "-readability-identifier-length",
         "-readability-named-parameter",
         "-readability-redundant-access-specifiers",
         "-readability-uppercase-literal-suffix",
@@ -113,6 +115,7 @@
         "libidmap2/proto/*.proto",
     ],
     host_supported: true,
+    tidy: false,
     proto: {
         type: "lite",
         export_proto_headers: true,
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index a8d6489..1b2d905 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -39,11 +39,9 @@
 #include "idmap2/Result.h"
 #include "idmap2/SysTrace.h"
 
-using android::IPCThreadState;
 using android::base::StringPrintf;
 using android::binder::Status;
 using android::idmap2::BinaryStreamVisitor;
-using android::idmap2::FabricatedOverlay;
 using android::idmap2::FabricatedOverlayContainer;
 using android::idmap2::Idmap;
 using android::idmap2::IdmapHeader;
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
index 2707049..dad09a0 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -15,14 +15,14 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
+#define LOG_TAG "idmap2d"
+#include "android-base/logging.h"
 
 #include <binder/BinderService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 
 #include <cstdlib>  // EXIT_{FAILURE,SUCCESS}
-#include <iostream>
-#include <sstream>
 
 #include "Idmap2Service.h"
 #include "android-base/macros.h"
@@ -35,14 +35,17 @@
 using android::os::Idmap2Service;
 
 int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
+  LOG(INFO) << "Starting";
   IPCThreadState::disableBackgroundScheduling(true);
   status_t ret = BinderService<Idmap2Service>::publish();
   if (ret != android::OK) {
+    LOG(ERROR) << "Failed to start: " << ret;
     return EXIT_FAILURE;
   }
   sp<ProcessState> ps(ProcessState::self());
   ps->startThreadPool();
   ps->giveThreadPoolName();
   IPCThreadState::self()->joinThreadPool();
+  LOG(INFO) << "Exiting";
   return EXIT_SUCCESS;
 }
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 1c82597..e1b7829 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -134,19 +134,19 @@
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
 
   ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
-                                             R::overlay::integer::int1)),
+                                                 R::overlay::integer::int1)),
             std::string::npos)
       << result->stdout_str;
   ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
-                                             R::overlay::string::str1)),
+                                                 R::overlay::string::str1)),
             std::string::npos)
       << result->stdout_str;
   ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
-                                             R::overlay::string::str3)),
+                                                 R::overlay::string::str3)),
             std::string::npos)
       << result->stdout_str;
   ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
-                                             R::overlay::string::str4)),
+                                                 R::overlay::string::str4)),
             std::string::npos)
       << result->stdout_str;
 
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index f2f8854..f9c4fa3 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -259,7 +259,8 @@
 }
 
 struct NoCopyContainer {
-  uint32_t value;  // NOLINT(misc-non-private-member-variables-in-classes)
+  uint32_t value = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
+  NoCopyContainer() = default;
   NoCopyContainer(const NoCopyContainer&) = delete;
   NoCopyContainer& operator=(const NoCopyContainer&) = delete;
 };
@@ -268,7 +269,7 @@
   if (!succeed) {
     return Error("foo");
   }
-  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U});
+  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{});
   p->value = 42U;
   return std::move(p);
 }
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 150ab99..ec0b79b 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -184,11 +184,26 @@
     sigemptyset(&child_mask);
     sigaddset(&child_mask, SIGCHLD);
 
+    // block SIGCHLD before we check if a process has exited
     if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
-        ALOGW("sigprocmask failed: %s", strerror(errno));
+        ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
         return false;
     }
 
+    // if the child has exited already, handle and reset signals before leaving
+    pid_t child_pid = waitpid(pid, status, WNOHANG);
+    if (child_pid != pid) {
+        if (child_pid > 0) {
+            ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+            sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+            return false;
+        }
+    } else {
+        sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+        return true;
+    }
+
+    // wait for a SIGCHLD
     timespec ts;
     ts.tv_sec = timeout_ms / 1000;
     ts.tv_nsec = (timeout_ms % 1000) * 1000000;
@@ -197,7 +212,7 @@
 
     // Set the signals back the way they were.
     if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
-        ALOGW("sigprocmask failed: %s", strerror(errno));
+        ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
         if (ret == 0) {
             return false;
         }
@@ -207,21 +222,21 @@
         if (errno == EAGAIN) {
             errno = ETIMEDOUT;
         } else {
-            ALOGW("sigtimedwait failed: %s", strerror(errno));
+            ALOGW("*** sigtimedwait failed: %s\n", strerror(errno));
         }
         return false;
     }
 
-    pid_t child_pid = waitpid(pid, status, WNOHANG);
-    if (child_pid == pid) {
-        return true;
+    child_pid = waitpid(pid, status, WNOHANG);
+    if (child_pid != pid) {
+        if (child_pid != -1) {
+            ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+        } else {
+            ALOGW("*** waitpid failed: %s\n", strerror(errno));
+        }
+        return false;
     }
-    if (child_pid == -1) {
-        ALOGW("waitpid failed: %s", strerror(errno));
-    } else {
-        ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid);
-    }
-    return false;
+    return true;
 }
 
 status_t kill_child(pid_t pid) {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 52f883b..50c2e75 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -52,7 +52,7 @@
 
       (new Telecom()).run(args);
     }
-
+    private static final String CALLING_PACKAGE = Telecom.class.getPackageName();
     private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
     private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
     private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
@@ -286,7 +286,7 @@
         final String label = nextArgRequired();
         PhoneAccount account = PhoneAccount.builder(handle, label)
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
-        mTelecomService.registerPhoneAccount(account);
+        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " registered.");
     }
 
@@ -316,7 +316,7 @@
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
                 .build();
-        mTelecomService.registerPhoneAccount(account);
+        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " registered.");
     }
 
@@ -358,7 +358,7 @@
 
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        mTelecomService.unregisterPhoneAccount(handle);
+        mTelecomService.unregisterPhoneAccount(handle, CALLING_PACKAGE);
         System.out.println("Success - " + handle + " unregistered.");
     }
 
@@ -395,11 +395,11 @@
     }
 
     private void runGetDefaultDialer() throws RemoteException {
-        System.out.println(mTelecomService.getDefaultDialerPackage());
+        System.out.println(mTelecomService.getDefaultDialerPackage(CALLING_PACKAGE));
     }
 
     private void runGetSystemDialer() throws RemoteException {
-        System.out.println(mTelecomService.getSystemDialerPackage());
+        System.out.println(mTelecomService.getSystemDialerPackage(CALLING_PACKAGE));
     }
 
     private void runWaitOnHandler() throws RemoteException {
diff --git a/core/api/current.txt b/core/api/current.txt
index 4d1e280..5dd85c0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -189,6 +189,7 @@
     field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES";
     field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
     field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
+    field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE";
     field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
     field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
@@ -631,6 +632,7 @@
     field public static final int elevation = 16843840; // 0x1010440
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
+    field public static final int enableOnBackInvokedCallback;
     field public static final int enableVrMode = 16844069; // 0x1010525
     field public static final int enabled = 16842766; // 0x101000e
     field public static final int end = 16843996; // 0x10104dc
@@ -1452,6 +1454,7 @@
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsBatteryGameMode;
     field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
+    field public static final int supportsInlineSuggestionsWithTouchExploration;
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
@@ -4274,6 +4277,7 @@
     method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
+    method public void setPreferDockBigOverlays(boolean);
     method @Deprecated public final void setProgress(int);
     method @Deprecated public final void setProgressBarIndeterminate(boolean);
     method @Deprecated public final void setProgressBarIndeterminateVisibility(boolean);
@@ -4547,6 +4551,7 @@
     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 @NonNull public static android.app.ActivityOptions makeLaunchIntoPip(@NonNull android.app.PictureInPictureParams);
     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>...);
@@ -5555,11 +5560,11 @@
 
   public final class GameState implements android.os.Parcelable {
     ctor public GameState(boolean, int);
-    ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle);
+    ctor public GameState(boolean, int, int, int);
     method public int describeContents();
-    method @Nullable public String getDescription();
-    method @NonNull public android.os.Bundle getMetadata();
+    method public int getLabel();
     method public int getMode();
+    method public int getQuality();
     method public boolean isLoading();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR;
@@ -5673,6 +5678,7 @@
   }
 
   public class KeyguardManager {
+    method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
     method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
     method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
     method @Deprecated public boolean inKeyguardRestrictedInputMode();
@@ -5681,6 +5687,7 @@
     method public boolean isKeyguardLocked();
     method public boolean isKeyguardSecure();
     method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String);
+    method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
     method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
   }
 
@@ -5696,6 +5703,10 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard();
   }
 
+  @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener {
+    method public void onKeyguardLockedStateChanged(boolean);
+  }
+
   @Deprecated public static interface KeyguardManager.OnKeyguardExitResult {
     method @Deprecated public void onKeyguardExitResult(boolean);
   }
@@ -6632,13 +6643,17 @@
 
   public static class PictureInPictureParams.Builder {
     ctor public PictureInPictureParams.Builder();
+    ctor public PictureInPictureParams.Builder(@NonNull android.app.PictureInPictureParams);
     method public android.app.PictureInPictureParams build();
     method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
     method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
     method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterEnabled(boolean);
+    method @NonNull public android.app.PictureInPictureParams.Builder setCloseAction(@Nullable android.app.RemoteAction);
     method @NonNull public android.app.PictureInPictureParams.Builder setExpandedAspectRatio(@Nullable android.util.Rational);
     method @NonNull public android.app.PictureInPictureParams.Builder setSeamlessResizeEnabled(boolean);
     method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
+    method @NonNull public android.app.PictureInPictureParams.Builder setSubtitle(@Nullable CharSequence);
+    method @NonNull public android.app.PictureInPictureParams.Builder setTitle(@Nullable CharSequence);
   }
 
   public final class PictureInPictureUiState implements android.os.Parcelable {
@@ -7792,7 +7807,6 @@
   }
 
   public final class DevicePolicyResources {
-    ctor public DevicePolicyResources();
   }
 
   public static final class DevicePolicyResources.Drawables {
@@ -12273,6 +12287,7 @@
     method @Nullable public java.util.Set<java.lang.String> getCategories();
     method @Nullable public CharSequence getDisabledMessage();
     method public int getDisabledReason();
+    method public int getExcludedFromSurfaces();
     method @Nullable public android.os.PersistableBundle getExtras();
     method @NonNull public String getId();
     method @Nullable public android.content.Intent getIntent();
@@ -12290,8 +12305,8 @@
     method public boolean isDeclaredInManifest();
     method public boolean isDynamic();
     method public boolean isEnabled();
+    method public boolean isExcludedFromSurfaces(int);
     method public boolean isImmutable();
-    method public boolean isIncludedIn(int);
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
@@ -14383,6 +14398,8 @@
     method @NonNull @Size(min=3) public abstract float[] fromXyz(@NonNull @Size(min=3) float[]);
     method @NonNull public static android.graphics.ColorSpace get(@NonNull android.graphics.ColorSpace.Named);
     method @IntRange(from=1, to=4) public int getComponentCount();
+    method public int getDataSpace();
+    method @Nullable public static android.graphics.ColorSpace getFromDataSpace(int);
     method @IntRange(from=android.graphics.ColorSpace.MIN_ID, to=android.graphics.ColorSpace.MAX_ID) public int getId();
     method public abstract float getMaxValue(@IntRange(from=0, to=3) int);
     method public abstract float getMinValue(@IntRange(from=0, to=3) int);
@@ -16384,12 +16401,8 @@
 package android.graphics.text {
 
   public final class LineBreakConfig {
-    ctor public LineBreakConfig();
     method public int getLineBreakStyle();
     method public int getLineBreakWordStyle();
-    method public void set(@NonNull android.graphics.text.LineBreakConfig);
-    method public void setLineBreakStyle(int);
-    method public void setLineBreakWordStyle(int);
     field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
     field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
     field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
@@ -16398,6 +16411,13 @@
     field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1
   }
 
+  public static final class LineBreakConfig.Builder {
+    ctor public LineBreakConfig.Builder();
+    method @NonNull public android.graphics.text.LineBreakConfig build();
+    method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakStyle(int);
+    method @NonNull public android.graphics.text.LineBreakConfig.Builder setLineBreakWordStyle(int);
+  }
+
   public class LineBreaker {
     method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
     field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16445,6 +16465,7 @@
   public class MeasuredText {
     method public void getBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Rect);
     method @FloatRange(from=0.0f) @Px public float getCharWidthAt(@IntRange(from=0) int);
+    method public void getFontMetricsInt(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Paint.FontMetricsInt);
     method @FloatRange(from=0.0) @Px public float getWidth(@IntRange(from=0) int, @IntRange(from=0) int);
   }
 
@@ -16861,6 +16882,7 @@
     field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L
     field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L
     field public static final int YCBCR_420_888 = 35; // 0x23
+    field public static final int YCBCR_P010 = 54; // 0x36
   }
 
   public final class Sensor {
@@ -16988,6 +17010,7 @@
 
   public class SensorEvent {
     field public int accuracy;
+    field public boolean firstEventAfterDiscontinuity;
     field public android.hardware.Sensor sensor;
     field public long timestamp;
     field public final float[] values;
@@ -16999,7 +17022,6 @@
     method public void onFlushCompleted(android.hardware.Sensor);
     method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
     method public void onSensorChanged(android.hardware.SensorEvent);
-    method public void onSensorDiscontinuity(@NonNull android.hardware.Sensor);
   }
 
   public interface SensorEventListener {
@@ -17117,6 +17139,9 @@
 
   public final class SensorPrivacyManager {
     method public boolean supportsSensorToggle(int);
+    method public boolean supportsSensorToggle(int, int);
+    field public static final int TOGGLE_TYPE_HARDWARE = 2; // 0x2
+    field public static final int TOGGLE_TYPE_SOFTWARE = 1; // 0x1
   }
 
   public static class SensorPrivacyManager.Sensors {
@@ -27433,7 +27458,6 @@
     field public static final String CATEGORY_PAYMENT = "payment";
     field public static final String EXTRA_CATEGORY = "category";
     field public static final String EXTRA_SERVICE_COMPONENT = "component";
-    field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
     field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
     field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
     field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
@@ -30673,7 +30697,7 @@
   public class BaseBundle {
     method public void clear();
     method public boolean containsKey(String);
-    method @Nullable public Object get(String);
+    method @Deprecated @Nullable public Object get(String);
     method public boolean getBoolean(String);
     method public boolean getBoolean(String, boolean);
     method @Nullable public boolean[] getBooleanArray(@Nullable String);
@@ -30915,16 +30939,21 @@
     method public float getFloat(String, float);
     method @Nullable public float[] getFloatArray(@Nullable String);
     method @Nullable public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
-    method @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
-    method @Nullable public java.io.Serializable getSerializable(@Nullable String);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
+    method @Nullable public <T> T getParcelable(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
+    method @Nullable public <T> T[] getParcelableArray(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
+    method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayList(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public java.io.Serializable getSerializable(@Nullable String);
+    method @Nullable public <T extends java.io.Serializable> T getSerializable(@Nullable String, @NonNull Class<T>);
     method public short getShort(String);
     method public short getShort(String, short);
     method @Nullable public short[] getShortArray(@Nullable String);
     method @Nullable public android.util.Size getSize(@Nullable String);
     method @Nullable public android.util.SizeF getSizeF(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+    method @Nullable public <T> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String, @NonNull Class<T>);
     method @Nullable public java.util.ArrayList<java.lang.String> getStringArrayList(@Nullable String);
     method public boolean hasFileDescriptors();
     method public void putAll(android.os.Bundle);
@@ -39504,7 +39533,7 @@
     method public void onCheckRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionService.SupportCallback);
     method protected abstract void onStartListening(android.content.Intent, android.speech.RecognitionService.Callback);
     method protected abstract void onStopListening(android.speech.RecognitionService.Callback);
-    method public void triggerModelDownload(@NonNull android.content.Intent);
+    method public void onTriggerModelDownload(@NonNull android.content.Intent);
     field public static final String SERVICE_INTERFACE = "android.speech.RecognitionService";
     field public static final String SERVICE_META_DATA = "android.speech";
   }
@@ -39561,12 +39590,21 @@
     field public static final String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
     field public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
     field public static final String DETAILS_META_DATA = "android.speech.DETAILS";
-    field public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+    field @Deprecated public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+    field public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+    field public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT = "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+    field public static final String EXTRA_AUDIO_SOURCE_ENCODING = "android.speech.extra.AUDIO_SOURCE_ENCODING";
+    field public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE = "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+    field public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
     field public static final String EXTRA_CALLING_PACKAGE = "calling_package";
     field public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
+    field public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT = "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+    field public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+    field public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION = "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
     field public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
     field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
     field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE";
+    field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS";
     field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
     field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
     field public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
@@ -39577,12 +39615,14 @@
     field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
     field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
     field public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
-    field public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+    field public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
     field public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
     field public static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final String EXTRA_SUPPORTED_LANGUAGES = "android.speech.extra.SUPPORTED_LANGUAGES";
     field public static final String EXTRA_WEB_SEARCH_ONLY = "android.speech.extra.WEB_SEARCH_ONLY";
+    field public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+    field public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
     field public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
     field public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
     field public static final int RESULT_AUDIO_ERROR = 5; // 0x5
@@ -39604,7 +39644,7 @@
 
   public class SpeechRecognizer {
     method @MainThread public void cancel();
-    method public void checkRecognitionSupport(@NonNull android.content.Intent, @NonNull android.speech.RecognitionSupportCallback);
+    method public void checkRecognitionSupport(@NonNull android.content.Intent, @NonNull java.util.concurrent.Executor, @NonNull android.speech.RecognitionSupportCallback);
     method @MainThread @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
     method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
     method @MainThread public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
@@ -45007,6 +45047,7 @@
     method public char charAt(int);
     method public static android.text.PrecomputedText create(@NonNull CharSequence, @NonNull android.text.PrecomputedText.Params);
     method public void getBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Rect);
+    method public void getFontMetricsInt(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.Paint.FontMetricsInt);
     method @IntRange(from=0) public int getParagraphCount();
     method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
     method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
@@ -45026,7 +45067,7 @@
   public static final class PrecomputedText.Params {
     method public int getBreakStrategy();
     method public int getHyphenationFrequency();
-    method @Nullable public android.graphics.text.LineBreakConfig getLineBreakConfig();
+    method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
     method @NonNull public android.text.TextDirectionHeuristic getTextDirection();
     method @NonNull public android.text.TextPaint getTextPaint();
   }
@@ -47676,15 +47717,11 @@
 
   public final class Choreographer {
     method public static android.view.Choreographer getInstance();
-    method public void postExtendedFrameCallback(@NonNull android.view.Choreographer.ExtendedFrameCallback);
     method public void postFrameCallback(android.view.Choreographer.FrameCallback);
     method public void postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long);
-    method public void removeExtendedFrameCallback(@Nullable android.view.Choreographer.ExtendedFrameCallback);
+    method public void postVsyncCallback(@NonNull android.view.Choreographer.VsyncCallback);
     method public void removeFrameCallback(android.view.Choreographer.FrameCallback);
-  }
-
-  public static interface Choreographer.ExtendedFrameCallback {
-    method public void onVsync(@NonNull android.view.Choreographer.FrameData);
+    method public void removeVsyncCallback(@Nullable android.view.Choreographer.VsyncCallback);
   }
 
   public static interface Choreographer.FrameCallback {
@@ -47699,10 +47736,14 @@
 
   public static class Choreographer.FrameTimeline {
     method public long getDeadlineNanos();
-    method public long getExpectedPresentTimeNanos();
+    method public long getExpectedPresentationTimeNanos();
     method public long getVsyncId();
   }
 
+  public static interface Choreographer.VsyncCallback {
+    method public void onVsync(@NonNull android.view.Choreographer.FrameData);
+  }
+
   public interface CollapsibleActionView {
     method public void onActionViewCollapsed();
     method public void onActionViewExpanded();
@@ -49254,6 +49295,7 @@
     method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
     method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
     method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer);
+    method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer, @Nullable android.hardware.SyncFence);
     method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
     method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int);
     method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
@@ -57333,7 +57375,8 @@
     method public final android.text.Layout getLayout();
     method public float getLetterSpacing();
     method public int getLineBounds(int, android.graphics.Rect);
-    method @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
+    method public int getLineBreakStyle();
+    method public int getLineBreakWordStyle();
     method public int getLineCount();
     method public int getLineHeight();
     method public float getLineSpacingExtra();
@@ -57461,7 +57504,8 @@
     method public void setKeyListener(android.text.method.KeyListener);
     method public void setLastBaselineToBottomHeight(@IntRange(from=0) @Px int);
     method public void setLetterSpacing(float);
-    method public void setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
+    method public void setLineBreakStyle(int);
+    method public void setLineBreakWordStyle(int);
     method public void setLineHeight(@IntRange(from=0) @Px int);
     method public void setLineSpacing(float, float);
     method public void setLines(int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b1f0b54..ea004c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -205,7 +205,6 @@
     field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
     field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
-    field public static final String MODIFY_TOUCH_MODE_STATE = "android.permission.MODIFY_TOUCH_MODE_STATE";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -1076,7 +1075,7 @@
   }
 
   public class DevicePolicyManager {
-    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
     method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
@@ -1105,7 +1104,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]);
-    method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
@@ -1129,21 +1128,6 @@
     field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
     field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
-    field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
-    field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
-    field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
-    field public static final int CODE_HAS_DEVICE_OWNER = 1; // 0x1
-    field public static final int CODE_HAS_PAIRED = 8; // 0x8
-    field public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
-    field public static final int CODE_NONSYSTEM_USER_EXISTS = 5; // 0x5
-    field public static final int CODE_NOT_SYSTEM_USER = 7; // 0x7
-    field public static final int CODE_OK = 0; // 0x0
-    field public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
-    field public static final int CODE_SYSTEM_USER = 10; // 0xa
-    field public static final int CODE_UNKNOWN_ERROR = -1; // 0xffffffff
-    field public static final int CODE_USER_HAS_PROFILE_OWNER = 2; // 0x2
-    field public static final int CODE_USER_NOT_RUNNING = 3; // 0x3
-    field public static final int CODE_USER_SETUP_COMPLETED = 4; // 0x4
     field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
     field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -1186,6 +1170,21 @@
     field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
     field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
     field public static final int STATE_USER_UNMANAGED = 0; // 0x0
+    field public static final int STATUS_ACCOUNTS_NOT_EMPTY = 6; // 0x6
+    field public static final int STATUS_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
+    field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
+    field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1
+    field public static final int STATUS_HAS_PAIRED = 8; // 0x8
+    field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
+    field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
+    field public static final int STATUS_NOT_SYSTEM_USER = 7; // 0x7
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15; // 0xf
+    field public static final int STATUS_SYSTEM_USER = 10; // 0xa
+    field public static final int STATUS_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int STATUS_USER_HAS_PROFILE_OWNER = 2; // 0x2
+    field public static final int STATUS_USER_NOT_RUNNING = 3; // 0x3
+    field public static final int STATUS_USER_SETUP_COMPLETED = 4; // 0x4
   }
 
   public static final class DevicePolicyResources.Strings {
@@ -1648,16 +1647,16 @@
 
   public final class SearchRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public String getCallerPackageName();
     method public float getMaxLatencyMillis();
     method @NonNull public String getQuery();
     method @NonNull public String getRequestId();
     method public int getResultNumber();
     method public int getResultOffset();
     method @NonNull public android.os.Bundle getSearchConstraints();
-    method @NonNull public String getSource();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION";
-    field public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER";
+    field public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "android.app.cloudsearch.IS_PRESUBMIT_SUGGESTION";
+    field public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "android.app.cloudsearch.SEARCH_PROVIDER_FILTER";
     field @NonNull public static final android.os.Parcelable.Creator<android.app.cloudsearch.SearchRequest> CREATOR;
   }
 
@@ -1699,23 +1698,25 @@
     method @NonNull public String getTitle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.cloudsearch.SearchResult> CREATOR;
-    field public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE";
-    field public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING = "ACTION_BUTTON_TEXT";
-    field public static final String EXTRAINFO_APP_BADGES = "APP_BADGES";
-    field public static final String EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER = "APP_CONTAINS_ADS_DISCLAIMER";
-    field public static final String EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER = "APP_CONTAINS_IAP_DISCLAIMER";
-    field public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME";
-    field public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL";
-    field public static final String EXTRAINFO_APP_IARC = "APP_IARC";
-    field public static final String EXTRAINFO_APP_ICON = "APP_ICON";
-    field public static final String EXTRAINFO_APP_REVIEW_COUNT = "APP_REVIEW_COUNT";
-    field public static final String EXTRAINFO_APP_SIZE_BYTES = "APP_SIZE_BYTES";
-    field public static final String EXTRAINFO_APP_STAR_RATING = "APP_STAR_RATING";
-    field public static final String EXTRAINFO_LONG_DESCRIPTION = "LONG_DESCRIPTION";
-    field public static final String EXTRAINFO_SCREENSHOTS = "SCREENSHOTS";
-    field public static final String EXTRAINFO_SHORT_DESCRIPTION = "SHORT_DESCRIPTION";
-    field public static final String EXTRAINFO_WEB_ICON = "WEB_ICON";
-    field public static final String EXTRAINFO_WEB_URL = "WEB_URL";
+    field public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "android.app.cloudsearch.ACTION_BUTTON_IMAGE";
+    field public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING = "android.app.cloudsearch.ACTION_BUTTON_TEXT";
+    field public static final String EXTRAINFO_APP_BADGES = "android.app.cloudsearch.APP_BADGES";
+    field public static final String EXTRAINFO_APP_CARD_ACTION = "android.app.cloudsearch.APP_CARD_ACTION";
+    field public static final String EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER = "android.app.cloudsearch.APP_CONTAINS_ADS_DISCLAIMER";
+    field public static final String EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER = "android.app.cloudsearch.APP_CONTAINS_IAP_DISCLAIMER";
+    field public static final String EXTRAINFO_APP_DEVELOPER_NAME = "android.app.cloudsearch.APP_DEVELOPER_NAME";
+    field public static final String EXTRAINFO_APP_DOMAIN_URL = "android.app.cloudsearch.APP_DOMAIN_URL";
+    field public static final String EXTRAINFO_APP_IARC = "android.app.cloudsearch.APP_IARC";
+    field public static final String EXTRAINFO_APP_ICON = "android.app.cloudsearch.APP_ICON";
+    field public static final String EXTRAINFO_APP_REVIEW_COUNT = "android.app.cloudsearch.APP_REVIEW_COUNT";
+    field public static final String EXTRAINFO_APP_SIZE_BYTES = "android.app.cloudsearch.APP_SIZE_BYTES";
+    field public static final String EXTRAINFO_APP_STAR_RATING = "android.app.cloudsearch.APP_STAR_RATING";
+    field public static final String EXTRAINFO_INSTALL_BUTTON_ACTION = "android.app.cloudsearch.INSTALL_BUTTON_ACTION";
+    field public static final String EXTRAINFO_LONG_DESCRIPTION = "android.app.cloudsearch.LONG_DESCRIPTION";
+    field public static final String EXTRAINFO_SCREENSHOTS = "android.app.cloudsearch.SCREENSHOTS";
+    field public static final String EXTRAINFO_SHORT_DESCRIPTION = "android.app.cloudsearch.SHORT_DESCRIPTION";
+    field public static final String EXTRAINFO_WEB_ICON = "android.app.cloudsearch.WEB_ICON";
+    field public static final String EXTRAINFO_WEB_URL = "android.app.cloudsearch.WEB_URL";
   }
 
   public static final class SearchResult.Builder {
@@ -2254,13 +2255,20 @@
 
   public class BaseTemplateData implements android.os.Parcelable {
     method public int describeContents();
+    method public int getLayoutWeight();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getPrimaryLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getPrimaryTapAction();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getSubtitleIcon();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSubtitleText();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalAlarmText();
+    method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalIcon();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalSubtitleIcon();
+    method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalSubtitleLoggingInfo();
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalSubtitleTapAction();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalSubtitleText();
+    method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalTapAction();
+    method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalText();
     method public int getTemplateType();
     method @Nullable public android.app.smartspace.uitemplatedata.Icon getTitleIcon();
     method @Nullable public android.app.smartspace.uitemplatedata.Text getTitleText();
@@ -2271,17 +2279,37 @@
   public static class BaseTemplateData.Builder {
     ctor public BaseTemplateData.Builder(int);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData build();
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setLayoutWeight(int);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.Text);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalText(@NonNull android.app.smartspace.uitemplatedata.Text);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon);
     method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.Text);
   }
 
+  public static final class BaseTemplateData.SubItemLoggingInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getFeatureType();
+    method public int getInstanceId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo> CREATOR;
+  }
+
+  public static final class BaseTemplateData.SubItemLoggingInfo.Builder {
+    ctor public BaseTemplateData.SubItemLoggingInfo.Builder(int, int);
+    method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo build();
+  }
+
   public final class CarouselTemplateData extends android.app.smartspace.uitemplatedata.BaseTemplateData {
     method @Nullable public android.app.smartspace.uitemplatedata.TapAction getCarouselAction();
     method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.CarouselTemplateData.CarouselItem> getCarouselItems();
@@ -2408,6 +2436,7 @@
     method @Nullable public android.content.Intent getIntent();
     method @Nullable public android.app.PendingIntent getPendingIntent();
     method @Nullable public android.os.UserHandle getUserHandle();
+    method public boolean shouldShowOnLockscreen();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.TapAction> CREATOR;
   }
@@ -2418,6 +2447,7 @@
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setIntent(@NonNull android.content.Intent);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setPendingIntent(@NonNull android.app.PendingIntent);
+    method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setShouldShowOnLockscreen(@NonNull boolean);
     method @NonNull public android.app.smartspace.uitemplatedata.TapAction.Builder setUserHandle(@Nullable android.os.UserHandle);
   }
 
@@ -2432,7 +2462,6 @@
 
   public static final class Text.Builder {
     ctor public Text.Builder(@NonNull CharSequence);
-    ctor public Text.Builder(@NonNull CharSequence, @NonNull android.text.TextUtils.TruncateAt);
     method @NonNull public android.app.smartspace.uitemplatedata.Text build();
     method @NonNull public android.app.smartspace.uitemplatedata.Text.Builder setMaxLines(int);
     method @NonNull public android.app.smartspace.uitemplatedata.Text.Builder setTruncateAtType(@NonNull android.text.TextUtils.TruncateAt);
@@ -2507,9 +2536,10 @@
 package android.app.usage {
 
   public final class BroadcastResponseStats implements android.os.Parcelable {
-    ctor public BroadcastResponseStats(@NonNull String);
+    ctor public BroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
     method public int describeContents();
     method @IntRange(from=0) public int getBroadcastsDispatchedCount();
+    method @IntRange(from=1) public long getId();
     method @IntRange(from=0) public int getNotificationsCancelledCount();
     method @IntRange(from=0) public int getNotificationsPostedCount();
     method @IntRange(from=0) public int getNotificationsUpdatedCount();
@@ -2566,13 +2596,13 @@
   }
 
   public final class UsageStatsManager {
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
     method @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long getLastTimeAnyComponentUsed(@NonNull String);
     method public int getUsageSource();
     method @RequiresPermission(android.Manifest.permission.BIND_CARRIER_SERVICES) public void onCarrierPrivilegedAppsChanged();
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.BroadcastResponseStats queryBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.List<android.app.usage.BroadcastResponseStats> queryBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
     method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
@@ -2765,7 +2795,7 @@
     method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
-    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -3730,13 +3760,25 @@
   public final class SensorPrivacyManager {
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
   }
 
   public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
-    method public void onSensorPrivacyChanged(int, boolean);
+    method public default void onSensorPrivacyChanged(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams);
+    method @Deprecated public void onSensorPrivacyChanged(int, boolean);
+  }
+
+  public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams {
+    method public int getSensor();
+    method public int getToggleType();
+    method public boolean isEnabled();
   }
 
 }
@@ -5318,9 +5360,9 @@
     method public int getCurrentDataRole();
     method public int getCurrentMode();
     method public int getCurrentPowerRole();
-    method public int getPowerBrickStatus();
+    method public int getPowerBrickConnectionStatus();
     method public int getSupportedRoleCombinations();
-    method @Nullable public int[] getUsbDataStatus();
+    method public int getUsbDataStatus();
     method public boolean isConnected();
     method public boolean isPowerTransferLimited();
     method public boolean isRoleCombinationSupported(int, int);
@@ -5329,6 +5371,13 @@
     field public static final int DATA_ROLE_DEVICE = 2; // 0x2
     field public static final int DATA_ROLE_HOST = 1; // 0x1
     field public static final int DATA_ROLE_NONE = 0; // 0x0
+    field public static final int DATA_STATUS_DISABLED_CONTAMINANT = 4; // 0x4
+    field public static final int DATA_STATUS_DISABLED_DEBUG = 32; // 0x20
+    field public static final int DATA_STATUS_DISABLED_DOCK = 8; // 0x8
+    field public static final int DATA_STATUS_DISABLED_FORCE = 16; // 0x10
+    field public static final int DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
+    field public static final int DATA_STATUS_ENABLED = 1; // 0x1
+    field public static final int DATA_STATUS_UNKNOWN = 0; // 0x0
     field public static final int MODE_AUDIO_ACCESSORY = 4; // 0x4
     field public static final int MODE_DEBUG_ACCESSORY = 8; // 0x8
     field public static final int MODE_DFP = 2; // 0x2
@@ -5340,13 +5389,6 @@
     field public static final int POWER_ROLE_NONE = 0; // 0x0
     field public static final int POWER_ROLE_SINK = 2; // 0x2
     field public static final int POWER_ROLE_SOURCE = 1; // 0x1
-    field public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3; // 0x3
-    field public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6; // 0x6
-    field public static final int USB_DATA_STATUS_DISABLED_DOCK = 4; // 0x4
-    field public static final int USB_DATA_STATUS_DISABLED_FORCE = 5; // 0x5
-    field public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
-    field public static final int USB_DATA_STATUS_ENABLED = 1; // 0x1
-    field public static final int USB_DATA_STATUS_UNKNOWN = 0; // 0x0
   }
 
 }
@@ -5849,8 +5891,8 @@
     method @IntRange(from=0, to=1023) public int getIssueOfDataClock();
     method @IntRange(from=0, to=255) public int getIssueOfDataEphemeris();
     method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef();
-    method @IntRange(from=0, to=604784) public int getTimeOfClock();
-    method @IntRange(from=0, to=604784) public int getTimeOfEphemeris();
+    method @IntRange(from=0) public long getTimeOfClock();
+    method @IntRange(from=0) public long getTimeOfEphemeris();
     method @FloatRange public double getTropoDelayMeters();
     method @Nullable public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
     method public boolean hasIono();
@@ -5877,8 +5919,8 @@
     method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataClock(@IntRange(from=0, to=1023) int);
     method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=255) int);
     method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef);
-    method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0, to=604784) int);
-    method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0, to=604784) int);
+    method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0) long);
+    method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0) int);
     method @NonNull public android.location.SatellitePvt.Builder setTropoDelayMeters(@FloatRange(from=0.0f, to=100.0f) double);
     method @NonNull public android.location.SatellitePvt.Builder setVelocityEcef(@NonNull android.location.SatellitePvt.VelocityEcef);
   }
@@ -6075,7 +6117,7 @@
     method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
-    method public static boolean isUltrasoundSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public boolean isUltrasoundSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
@@ -6748,7 +6790,8 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) 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 @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int, @Nullable String);
+    method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int, @NonNull String);
+    method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int);
     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();
     method @Nullable @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) public android.os.IBinder getExtensionInterface(@NonNull String, @NonNull String);
@@ -10003,6 +10046,21 @@
     field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
+  public final class PermissionGroupUsage implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public CharSequence getAttributionLabel();
+    method @Nullable public CharSequence getAttributionTag();
+    method public long getLastAccessTimeMillis();
+    method @NonNull public String getPackageName();
+    method @NonNull public String getPermissionGroupName();
+    method @Nullable public CharSequence getProxyLabel();
+    method public int getUid();
+    method public boolean isActive();
+    method public boolean isPhoneCall();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.permission.PermissionGroupUsage> CREATOR;
+  }
+
   public final class PermissionManager {
     method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
     method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
@@ -10019,6 +10077,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS";
+    field public static final String EXTRA_PERMISSION_USAGES = "android.permission.extra.PERMISSION_USAGES";
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int PERMISSION_HARD_DENIED = 2; // 0x2
     field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
@@ -10847,7 +10906,7 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback);
     method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback);
-    method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback);
+    method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityUpdateCallback);
     method public void onStopProximityUpdates();
     field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6
     field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3
@@ -10865,7 +10924,7 @@
     method public void onSuccess(int, long);
   }
 
-  public static final class AttentionService.ProximityCallback {
+  public static final class AttentionService.ProximityUpdateCallback {
     method public void onProximityUpdate(double);
   }
 
@@ -11692,8 +11751,6 @@
 
   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);
   }
 
@@ -15621,7 +15678,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public android.graphics.Region getCurrentImeTouchRegion();
-    method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull android.window.TaskFpsCallback);
+    method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback);
     method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
   }
 
@@ -16213,12 +16270,9 @@
 
 package android.window {
 
-  public final class TaskFpsCallback {
-    ctor public TaskFpsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback.OnFpsCallbackListener);
-  }
-
-  public static interface TaskFpsCallback.OnFpsCallbackListener {
-    method public void onFpsReported(float);
+  public abstract class TaskFpsCallback {
+    ctor public TaskFpsCallback();
+    method public abstract void onFpsReported(float);
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5c5321c..6ebf188 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -107,6 +107,8 @@
     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);
+    field public static final String DUMP_ARG_DUMP_DUMPABLE = "--dump-dumpable";
+    field public static final String DUMP_ARG_LIST_DUMPABLES = "--list-dumpables";
   }
 
   public class ActivityManager {
@@ -270,9 +272,12 @@
   }
 
   public class DreamManager {
+    method public boolean areDreamsSupported();
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isDreaming();
+    method public boolean isScreensaverEnabled();
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@Nullable android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setDreamOverlay(@Nullable android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setScreensaverEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@NonNull android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
   }
@@ -352,8 +357,11 @@
   public final class PictureInPictureParams implements android.os.Parcelable {
     method public java.util.List<android.app.RemoteAction> getActions();
     method public float getAspectRatio();
+    method @Nullable public android.app.RemoteAction getCloseAction();
     method public float getExpandedAspectRatio();
     method public android.graphics.Rect getSourceRectHint();
+    method @Nullable public CharSequence getSubtitle();
+    method @Nullable public CharSequence getTitle();
     method public boolean isSeamlessResizeEnabled();
   }
 
@@ -411,6 +419,7 @@
     method @NonNull public android.content.res.Configuration getConfiguration();
     method public int getParentTaskId();
     method @Nullable public android.app.PictureInPictureParams getPictureInPictureParams();
+    method public boolean getPreferDockBigOverlays();
     method @NonNull public android.window.WindowContainerToken getToken();
     method public boolean hasParentTask();
   }
@@ -516,7 +525,6 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
     field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
-    field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
     field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0
     field public static final int DEVICE_OWNER_TYPE_FINANCED = 1; // 0x1
     field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17
@@ -560,6 +568,7 @@
     field public static final int OPERATION_SWITCH_USER = 2; // 0x2
     field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
     field public static final int OPERATION_WIPE_DATA = 8; // 0x8
+    field @Deprecated public static final int STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
   }
 
   public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -609,7 +618,7 @@
 package android.app.cloudsearch {
 
   public static final class SearchRequest.Builder {
-    method @NonNull public android.app.cloudsearch.SearchRequest.Builder setSource(@NonNull String);
+    method @NonNull public android.app.cloudsearch.SearchRequest.Builder setCallerPackageName(@NonNull String);
   }
 
 }
@@ -1453,6 +1462,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
     method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
     method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+    method @Nullable public static String getHalVersion();
     method public static final int[] getPublicStreamTypes();
     method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
     method public int getStreamMinVolumeInt(int);
@@ -2049,17 +2059,6 @@
 
 package android.permission {
 
-  public final class PermGroupUsage {
-    ctor public PermGroupUsage(@NonNull String, int, @NonNull String, long, boolean, boolean, @Nullable CharSequence);
-    method @Nullable public CharSequence getAttribution();
-    method public long getLastAccess();
-    method @NonNull public String getPackageName();
-    method @NonNull public String getPermGroupName();
-    method public int getUid();
-    method public boolean isActive();
-    method public boolean isPhoneCall();
-  }
-
   public final class PermissionControllerManager {
     method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
@@ -2075,8 +2074,8 @@
   }
 
   public final class PermissionManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
-    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData();
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData(boolean);
     method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
     method @RequiresPermission(android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL) public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
   }
@@ -2982,6 +2981,7 @@
     field public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
     field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled";
     field public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = "text_change_flush_frequency";
+    field public static final String DUMPABLE_NAME = "ContentCaptureManager";
     field public static final int LOGGING_LEVEL_DEBUG = 1; // 0x1
     field public static final int LOGGING_LEVEL_OFF = 0; // 0x0
     field public static final int LOGGING_LEVEL_VERBOSE = 2; // 0x2
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index eaf79b4..11663a5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -814,6 +814,13 @@
         Dialog mDialog;
         Bundle mArgs;
     }
+
+    /** @hide */ public static final String DUMP_ARG_AUTOFILL = "--autofill";
+    /** @hide */ public static final String DUMP_ARG_CONTENT_CAPTURE = "--contentcapture";
+    /** @hide */ public static final String DUMP_ARG_TRANSLATION = "--translation";
+    /** @hide */ @TestApi public static final String DUMP_ARG_LIST_DUMPABLES = "--list-dumpables";
+    /** @hide */ @TestApi public static final String DUMP_ARG_DUMP_DUMPABLE = "--dump-dumpable";
+
     private SparseArray<ManagedDialog> mManagedDialogs;
 
     // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.
@@ -1649,7 +1656,10 @@
         }
         mRestoredFromBundle = savedInstanceState != null;
         mCalled = true;
-        if (!WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+
+        boolean aheadOfTimeBack = WindowOnBackInvokedDispatcher
+                .isOnBackInvokedCallbackEnabled(this);
+        if (aheadOfTimeBack) {
             // Add onBackPressed as default back behavior.
             mDefaultBackCallback = new OnBackInvokedCallback() {
                 @Override
@@ -2955,6 +2965,27 @@
         return false;
     }
 
+    /**
+     * Specifies a preference to dock big overlays like the expanded picture-in-picture on TV
+     * (see {@link PictureInPictureParams.Builder#setExpandedAspectRatio}). Docking puts the
+     * big overlay side-by-side next to this activity, so that both windows are fully visible to
+     * the user.
+     *
+     * <p> If unspecified, whether the overlay window will be docked or not, will be defined
+     * by the system.
+     *
+     * <p> If specified, the system will try to respect the preference, but it may be
+     * overridden by a user preference.
+     *
+     * @param preferDockBigOverlays indicates that the activity prefers big overlays to be
+     *                              docked next to it instead of overlaying its content
+     *
+     * @see PictureInPictureParams.Builder#setExpandedAspectRatio
+     */
+    public void setPreferDockBigOverlays(boolean preferDockBigOverlays) {
+        ActivityClient.getInstance().setPreferDockBigOverlays(mToken, preferDockBigOverlays);
+    }
+
     void dispatchMovedToDisplay(int displayId, Configuration config) {
         updateDisplay(displayId);
         onMovedToDisplay(displayId, config);
@@ -7367,25 +7398,30 @@
             boolean isSpecialCase = true;
             // Handle special cases
             switch (arg) {
-                case "--autofill":
-                    dumpAutofillManager(prefix, writer, args);
-                    break;
-                case "--contentcapture":
-                    dumpContentCaptureManager(prefix, writer);
-                    break;
-                case "--translation":
-                    dumpUiTranslation(prefix, writer);
-                    break;
-                case "--list-dumpables":
+                case DUMP_ARG_AUTOFILL:
+                    dumpLegacyDumpable(prefix, writer, arg,
+                            AutofillClientController.DUMPABLE_NAME);
+                    return;
+                case DUMP_ARG_CONTENT_CAPTURE:
+                    dumpLegacyDumpable(prefix, writer, arg,
+                            ContentCaptureManager.DUMPABLE_NAME);
+                    return;
+                case DUMP_ARG_TRANSLATION:
+                    dumpLegacyDumpable(prefix, writer, arg,
+                            UiTranslationController.DUMPABLE_NAME);
+                    return;
+                case DUMP_ARG_LIST_DUMPABLES:
                     if (mDumpableContainer == null) {
                         writer.print(prefix); writer.println("No dumpables");
                     } else {
                         mDumpableContainer.listDumpables(prefix, writer);
                     }
-                    break;
-                case "--dump-dumpable":
+                    mDumpableContainer.listDumpables(prefix, writer);
+                    return;
+                case DUMP_ARG_DUMP_DUMPABLE:
                     if (args.length == 1) {
-                        writer.println("--dump-dumpable requires the dumpable name");
+                        writer.print(DUMP_ARG_DUMP_DUMPABLE);
+                        writer.println(" requires the dumpable name");
                     } else if (mDumpableContainer == null) {
                         writer.println("no dumpables");
                     } else {
@@ -7452,25 +7488,10 @@
         }
     }
 
-    private void dumpContentCaptureManager(String prefix, PrintWriter writer) {
-        dumpLegacyDumpable(prefix, writer, ContentCaptureManager.DUMPABLE_NAME, /* args= */ null);
-    }
-
-    private void dumpUiTranslation(String prefix, PrintWriter writer) {
-        dumpLegacyDumpable(prefix, writer, UiTranslationController.DUMPABLE_NAME, /* args= */ null);
-    }
-
-    private void dumpAutofillManager(String prefix, PrintWriter writer, String[] args) {
-        dumpLegacyDumpable(prefix, writer, AutofillClientController.DUMPABLE_NAME, args);
-    }
-
-    private void dumpLegacyDumpable(@NonNull String prefix, @NonNull PrintWriter writer,
-            @NonNull String dumpableName, @Nullable String[] args) {
-        if (mDumpableContainer == null) {
-            writer.print(prefix); writer.print("no "); writer.println(dumpableName);
-            return;
-        }
-        mDumpableContainer.dumpOneDumpable(prefix, writer, dumpableName, args);
+    private void dumpLegacyDumpable(String prefix, PrintWriter writer, String legacyOption,
+            String dumpableName) {
+        writer.printf("%s%s option deprecated. Use %s %s instead\n", prefix, legacyOption,
+                DUMP_ARG_DUMP_DUMPABLE, dumpableName);
     }
 
     /**
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 4715e0f..cf8480c 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -324,6 +324,14 @@
         }
     }
 
+    void setPreferDockBigOverlays(IBinder token, boolean preferDockBigOverlays) {
+        try {
+            getActivityClientController().setPreferDockBigOverlays(token, preferDockBigOverlays);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     void toggleFreeformWindowingMode(IBinder token) {
         try {
             getActivityClientController().toggleFreeformWindowingMode(token);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 89dd9ef..9f15105 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -179,6 +179,12 @@
      * @hide
      */
     public static final int INSTR_FLAG_NO_RESTART = 1 << 3;
+    /**
+     * Force the check that instrumentation and the target package are signed with the same
+     * certificate even if {@link Build#IS_DEBUGGABLE} is {@code true}.
+     * @hide
+     */
+    public static final int INSTR_FLAG_ALWAYS_CHECK_SIGNATURE = 1 << 4;
 
     static final class UidObserver extends IUidObserver.Stub {
         final OnUidImportanceListener mListener;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d29d3d4..9fe8e79 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -831,4 +831,9 @@
      * Restart android.
      */
     public abstract void restart();
+
+    /**
+     * Returns some summary statistics of the current PendingIntent queue - sizes and counts.
+     */
+    public abstract List<PendingIntentStats> getPendingIntentStats();
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 5012121..5ddaa80 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -350,6 +350,10 @@
     /** See {@link #setTransientLaunch()}. */
     private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
 
+    /** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */
+    private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
+            "android.activity.launchIntoPipParams";
+
     /**
      * @see #setLaunchCookie
      * @hide
@@ -444,6 +448,7 @@
     private boolean mRemoveWithTaskOrganizer;
     private boolean mLaunchedFromBubble;
     private boolean mTransientLaunch;
+    private PictureInPictureParams mLaunchIntoPipParams;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -1106,6 +1111,24 @@
         return opts;
     }
 
+    /**
+     * Creates an {@link ActivityOptions} instance that launch into picture-in-picture.
+     * This is normally used by a Host activity to start another activity that will directly enter
+     * picture-in-picture upon its creation.
+     * @param pictureInPictureParams {@link PictureInPictureParams} for launching the Activity to
+     *                               picture-in-picture mode.
+     */
+    @NonNull
+    public static ActivityOptions makeLaunchIntoPip(
+            @NonNull PictureInPictureParams pictureInPictureParams) {
+        final ActivityOptions opts = new ActivityOptions();
+        opts.mLaunchIntoPipParams = new PictureInPictureParams.Builder(pictureInPictureParams)
+                .setIsLaunchIntoPip(true)
+                .build();
+        opts.mLaunchBounds = new Rect(pictureInPictureParams.getSourceRectHint());
+        return opts;
+    }
+
     /** @hide */
     public boolean getLaunchTaskBehind() {
         return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
@@ -1219,6 +1242,7 @@
         mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
         mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
         mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
+        mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
     }
 
     /**
@@ -1556,6 +1580,23 @@
         mLaunchWindowingMode = windowingMode;
     }
 
+    /**
+     * @return {@link PictureInPictureParams} used to launch into PiP mode.
+     * @hide
+     */
+    public PictureInPictureParams getLaunchIntoPipParams() {
+        return mLaunchIntoPipParams;
+    }
+
+    /**
+     * @return {@code true} if this instance is used to launch into PiP mode.
+     * @hide
+     */
+    public boolean isLaunchIntoPip() {
+        return mLaunchIntoPipParams != null
+                && mLaunchIntoPipParams.isLaunchIntoPip();
+    }
+
     /** @hide */
     public int getLaunchActivityType() {
         return mLaunchActivityType;
@@ -1867,6 +1908,7 @@
         mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
         mSpecsFuture = otherOptions.mSpecsFuture;
         mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
+        mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
     }
 
     /**
@@ -2039,6 +2081,9 @@
         if (mSplashScreenStyle != 0) {
             b.putInt(KEY_SPLASH_SCREEN_STYLE, mSplashScreenStyle);
         }
+        if (mLaunchIntoPipParams != null) {
+            b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
+        }
         return b;
     }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0d1bc05..7c7c7ef 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2687,7 +2687,7 @@
             AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
             AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
             AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
-            getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
+            AppOpsManager.MODE_DEFAULT, // SYSTEM_ALERT_WINDOW /*Overridden in opToDefaultMode()*/
             AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
             AppOpsManager.MODE_ALLOWED, // CAMERA
             AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
@@ -3011,6 +3011,8 @@
     private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
     private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
 
+    private static volatile Integer sOpSystemAlertWindowDefaultMode;
+
     /**
      * Retrieve the op switch that controls the given operation.
      * @hide
@@ -3109,6 +3111,9 @@
      * @hide
      */
     public static @Mode int opToDefaultMode(int op) {
+        if (op == OP_SYSTEM_ALERT_WINDOW) {
+            return getSystemAlertWindowDefault();
+        }
         return sOpDefaultMode[op];
     }
 
@@ -10113,6 +10118,11 @@
     }
 
     private static int getSystemAlertWindowDefault() {
+        // This is indeed racy but we aren't expecting the result to change so it's not worth
+        // the synchronization.
+        if (sOpSystemAlertWindowDefaultMode != null) {
+            return sOpSystemAlertWindowDefaultMode;
+        }
         final Context context = ActivityThread.currentApplication();
         if (context == null) {
             return AppOpsManager.MODE_DEFAULT;
@@ -10123,10 +10133,11 @@
         // TVs are constantly plugged in and has less concern for memory/power
         if (ActivityManager.isLowRamDeviceStatic()
                 && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0)) {
-            return AppOpsManager.MODE_IGNORED;
+            sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_IGNORED;
+        } else {
+            sOpSystemAlertWindowDefaultMode = AppOpsManager.MODE_DEFAULT;
         }
-
-        return AppOpsManager.MODE_DEFAULT;
+        return sOpSystemAlertWindowDefaultMode;
     }
 
     /**
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index aa6c184..569fda9 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -456,7 +456,8 @@
      */
     protected void onStart() {
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
-        if (mContext != null && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+        if (mContext != null
+                && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
             // Add onBackPressed as default back behavior.
             mDefaultBackCallback = new OnBackInvokedCallback() {
                 @Override
@@ -703,7 +704,7 @@
         if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
                 && event.isTracking()
                 && !event.isCanceled()
-                && WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+                && !WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
             onBackPressed();
             return true;
         }
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index 34ae08fd..00af1c02 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -26,6 +28,8 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 
@@ -48,6 +52,40 @@
     }
 
     /**
+     * Returns whether Settings.Secure.SCREENSAVER_ENABLED is enabled.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean isScreensaverEnabled() {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
+    /**
+     * Sets whether Settings.Secure.SCREENSAVER_ENABLED is enabled.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public void setScreensaverEnabled(boolean enabled) {
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0, UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Returns whether dreams are supported.
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean areDreamsSupported() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsSupported);
+    }
+
+    /**
      * Starts dream service with name "name".
      *
      * <p>This is only used for testing the dream service APIs.
diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java
index 979dd34..fe6e554 100644
--- a/core/java/android/app/GameState.java
+++ b/core/java/android/app/GameState.java
@@ -18,8 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -88,12 +86,11 @@
     // One of the states listed above.
     private final @GameStateMode int mMode;
 
-    // This is a game specific description. For example can be level or scene name.
-    private final @Nullable String mDescription;
+    // A developer-supplied enum, e.g. to indicate level or scene.
+    private final int mLabel;
 
-    // This contains any other game specific parameters not covered by the fields above. It can be
-    // quality parameter data, settings, or game modes.
-    private final @NonNull Bundle mMetaData;
+    // The developer-supplied enum, e.g. to indicate the current quality level.
+    private final int mQuality;
 
     /**
      * Create a GameState with the specified loading status.
@@ -101,29 +98,28 @@
      * @param mode The game state mode of type @GameStateMode.
      */
     public GameState(boolean isLoading, @GameStateMode int mode) {
-        this(isLoading, mode, null, new Bundle());
+        this(isLoading, mode, -1, -1);
     }
 
     /**
      * Create a GameState with the given state variables.
      * @param isLoading Whether the game is in the loading state.
-     * @param mode The game state mode of type @GameStateMode.
-     * @param description An optional description of the state.
-     * @param metaData Optional metadata.
+     * @param mode The game state mode.
+     * @param label An optional developer-supplied enum e.g. for the current level.
+     * @param quality An optional developer-supplied enum, e.g. for the current quality level.
      */
-    public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description,
-            @NonNull Bundle metaData) {
+    public GameState(boolean isLoading, @GameStateMode int mode, int label, int quality) {
         mIsLoading = isLoading;
         mMode = mode;
-        mDescription = description;
-        mMetaData = metaData;
+        mLabel = label;
+        mQuality = quality;
     }
 
     private GameState(Parcel in) {
         mIsLoading = in.readBoolean();
         mMode = in.readInt();
-        mDescription = in.readString();
-        mMetaData = in.readBundle();
+        mLabel = in.readInt();
+        mQuality = in.readInt();
     }
 
     /**
@@ -141,17 +137,19 @@
     }
 
     /**
-     * @return The state description, or null if one is not set.
+     * @return The developer-supplied enum, e.g. to indicate level or scene. The default value (if
+     * not supplied) is -1.
      */
-    public @Nullable String getDescription() {
-        return mDescription;
+    public int getLabel() {
+        return mLabel;
     }
 
     /**
-     * @return metadata associated with the state.
+     * @return The developer-supplied enum, e.g. to indicate the current quality level. The default
+     * value (if not suplied) is -1.
      */
-    public @NonNull Bundle getMetadata() {
-        return mMetaData;
+    public int getQuality() {
+        return mQuality;
     }
 
     @Override
@@ -163,8 +161,8 @@
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeBoolean(mIsLoading);
         parcel.writeInt(mMode);
-        parcel.writeString(mDescription);
-        parcel.writeBundle(mMetaData);
+        parcel.writeInt(mLabel);
+        parcel.writeInt(mQuality);
     }
 
     /**
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index f9439cb..caf1c41b7 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -88,6 +88,7 @@
 
     boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
     void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
+    oneway void setPreferDockBigOverlays(in IBinder token, in boolean preferDockBigOverlays);
     void toggleFreeformWindowingMode(in IBinder token);
 
     oneway void startLockTaskModeByToken(in IBinder token);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7c48a57..fe0edfe 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -510,7 +510,6 @@
     void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
     @UnsupportedAppUsage
     int getPackageProcessState(in String packageName, in String callingPackage);
-    void updateDeviceOwner(in String packageName);
 
     // Start of N transactions
     // Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 55afed2..2242224b 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -73,6 +73,7 @@
      * @param nightModeCustomType
      * @hide
      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)")
     void setNightModeCustomType(int nightModeCustomType);
 
     /**
@@ -82,6 +83,7 @@
      * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
      * @hide
      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)")
     int getNightModeCustomType();
 
     /**
@@ -113,6 +115,7 @@
      *         {@code nightModeCustomType}.
      * @hide
      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)")
     boolean setNightModeActivatedForCustomMode(int nightModeCustom, boolean active);
 
     /**
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 9910000..87ba197 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -52,6 +52,7 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
 import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
@@ -183,6 +184,10 @@
     })
     @interface LockTypes {}
 
+    // TODO(b/220379118): register only one binder listener and keep a map of listener to executor.
+    private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener>
+            mKeyguardLockedStateListeners = new ArrayMap<>();
+
     /**
      * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics
      * if enrolled) for the current user of the device. The caller is expected to launch this
@@ -534,7 +539,7 @@
     /**
      * Return whether the keyguard is currently locked.
      *
-     * @return true if keyguard is locked.
+     * @return {@code true} if the keyguard is locked.
      */
     public boolean isKeyguardLocked() {
         try {
@@ -550,7 +555,7 @@
      *
      * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
      *
-     * @return true if a PIN, pattern or password is set or a SIM card is locked.
+     * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
      */
     public boolean isKeyguardSecure() {
         try {
@@ -565,7 +570,7 @@
      * keyguard password emergency screen). When in such mode, certain keys,
      * such as the Home key and the right soft keys, don't work.
      *
-     * @return true if in keyguard restricted input mode.
+     * @return {@code true} if in keyguard restricted input mode.
      * @deprecated Use {@link #isKeyguardLocked()} instead.
      */
     public boolean inKeyguardRestrictedInputMode() {
@@ -576,7 +581,7 @@
      * Returns whether the device is currently locked and requires a PIN, pattern or
      * password to unlock.
      *
-     * @return true if unlocking the device currently requires a PIN, pattern or
+     * @return {@code true} if unlocking the device currently requires a PIN, pattern or
      * password.
      */
     public boolean isDeviceLocked() {
@@ -603,7 +608,7 @@
      *
      * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
      *
-     * @return true if a PIN, pattern or password was set.
+     * @return {@code true} if a PIN, pattern or password was set.
      */
     public boolean isDeviceSecure() {
         return isDeviceSecure(mContext.getUserId());
@@ -762,7 +767,7 @@
     *        as the output of String#getBytes
     * @param complexity - complexity level imposed by the requester
     *        as defined in {@code DevicePolicyManager.PasswordComplexity}
-    * @return true if the password is valid, false otherwise
+    * @return {@code true} if the password is valid, false otherwise
     * @hide
     */
     @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -821,7 +826,7 @@
     *        as the output of String#getBytes
     * @param complexity - complexity level imposed by the requester
     *        as defined in {@code DevicePolicyManager.PasswordComplexity}
-    * @return true if the lock is successfully set, false otherwise
+    * @return {@code true} if the lock is successfully set, false otherwise
     * @hide
     */
     @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -903,8 +908,8 @@
     /**
      * Remove a weak escrow token.
      *
-     * @return true if the given handle refers to a valid weak token previously returned from
-     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     * @return {@code true} if the given handle refers to a valid weak token previously returned
+     * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -944,7 +949,7 @@
     /**
      * Register the given WeakEscrowTokenRemovedListener.
      *
-     * @return true if the listener is registered successfully, return false otherwise.
+     * @return {@code true} if the listener is registered successfully, return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -982,7 +987,7 @@
     /**
      * Unregister the given WeakEscrowTokenRemovedListener.
      *
-     * @return true if the listener is unregistered successfully, return false otherwise.
+     * @return {@code true} if the listener is unregistered successfully, return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -1076,4 +1081,61 @@
                 throw new IllegalArgumentException("Unknown lock type " + lockType);
         }
     }
+
+    /**
+     * Listener for keyguard locked state changes.
+     */
+    @FunctionalInterface
+    public interface KeyguardLockedStateListener {
+        /**
+         * Callback function that executes when the keyguard locked state changes.
+         */
+        void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+    }
+
+    /**
+     * Registers a listener to execute when the keyguard visibility changes.
+     *
+     * @param listener The listener to add to receive keyguard visibility changes.
+     */
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull KeyguardLockedStateListener listener) {
+        synchronized (mKeyguardLockedStateListeners) {
+            try {
+                final IKeyguardLockedStateListener innerListener =
+                        new IKeyguardLockedStateListener.Stub() {
+                    @Override
+                    public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+                        executor.execute(
+                                () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked));
+                    }
+                };
+                mWM.addKeyguardLockedStateListener(innerListener);
+                mKeyguardLockedStateListeners.put(listener, innerListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters a listener that executes when the keyguard visibility changes.
+     */
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) {
+        synchronized (mKeyguardLockedStateListeners) {
+            IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get(
+                    listener);
+            if (innerListener == null) {
+                return;
+            }
+            try {
+                mWM.removeKeyguardLockedStateListener(innerListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mKeyguardLockedStateListeners.remove(listener);
+        }
+    }
 }
diff --git a/core/java/android/window/IOnFpsCallbackListener.aidl b/core/java/android/app/PendingIntentStats.java
similarity index 63%
copy from core/java/android/window/IOnFpsCallbackListener.aidl
copy to core/java/android/app/PendingIntentStats.java
index 3091df3..111db3c 100644
--- a/core/java/android/window/IOnFpsCallbackListener.aidl
+++ b/core/java/android/app/PendingIntentStats.java
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-package android.window;
+package android.app;
 
 /**
+ * Data about pending intents - size and count, per UID that sent the intent.
  * @hide
  */
-oneway interface IOnFpsCallbackListener {
+public class PendingIntentStats {
+    public final int uid;
+    public final int count;
+    public final int sizeKb;
 
-    /**
-     * Reports the fps from the registered task
-     * @param fps The frame rate per second of the task that has the registered task id
-     *            and its children.
-     */
-    void onFpsReported(in float fps);
+    public PendingIntentStats(int uid, int count, int sizeKb) {
+        this.uid = uid;
+        this.count = count;
+        this.sizeKb = sizeKb;
+    }
 }
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 18343fd..2d2788c 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -51,19 +51,46 @@
         private List<RemoteAction> mUserActions;
 
         @Nullable
+        private RemoteAction mCloseAction;
+
+        @Nullable
         private Rect mSourceRectHint;
 
         private Boolean mAutoEnterEnabled;
 
         private Boolean mSeamlessResizeEnabled;
 
+        private CharSequence mTitle;
+
+        private CharSequence mSubtitle;
+
+        private Boolean mIsLaunchIntoPip;
+
+        /** Default constructor */
+        public Builder() {}
+
+        /**
+         * Copy constructor
+         * @param original {@link PictureInPictureParams} instance this builder is built upon.
+         */
+        public Builder(@NonNull PictureInPictureParams original) {
+            mAspectRatio = original.mAspectRatio;
+            mUserActions = original.mUserActions;
+            mCloseAction = original.mCloseAction;
+            mSourceRectHint = original.mSourceRectHint;
+            mAutoEnterEnabled = original.mAutoEnterEnabled;
+            mSeamlessResizeEnabled = original.mSeamlessResizeEnabled;
+            mTitle = original.mTitle;
+            mSubtitle = original.mSubtitle;
+            mIsLaunchIntoPip = original.mIsLaunchIntoPip;
+        }
+
         /**
          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
          * does not change upon device rotation.
          *
          * @param aspectRatio the new aspect ratio for the activity in picture-in-picture, must be
-         * between 2.39:1 and 1:2.39 (inclusive).
-         *
+         *                    between 2.39:1 and 1:2.39 (inclusive).
          * @return this builder instance.
          */
         public Builder setAspectRatio(Rational aspectRatio) {
@@ -111,6 +138,25 @@
         }
 
         /**
+         * Sets a close action that should be invoked before the default close PiP action. The
+         * custom action must close the activity quickly using {@link Activity#finish()}.
+         * Otherwise, the system will forcibly close the PiP as if no custom close action was
+         * provided.
+         *
+         * If the action matches one set via {@link PictureInPictureParams.Builder#setActions(List)}
+         * it may be shown in place of that custom action in the menu.
+         *
+         * @param action to replace the system close action
+         * @return this builder instance.
+         * @see RemoteAction
+         */
+        @NonNull
+        public Builder setCloseAction(@Nullable RemoteAction action) {
+            mCloseAction = action;
+            return this;
+        }
+
+        /**
          * Sets the source bounds hint. These bounds are only used when an activity first enters
          * picture-in-picture, and describe the bounds in window coordinates of activity entering
          * picture-in-picture that will be visible following the transition. For the best effect,
@@ -168,6 +214,50 @@
         }
 
         /**
+         * Sets a title for the picture-in-picture window, which may be displayed by the system to
+         * give the user information about what this PIP is generally being used for.
+         *
+         * @param title General information about the PIP content
+         * @return this builder instance.
+         */
+        @NonNull
+        public Builder setTitle(@Nullable CharSequence title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets a subtitle for the picture-in-picture window, which may be displayed by the system
+         * to give the user more detailed information about what this PIP is displaying.<br/>
+         *
+         * Setting a title via {@link PictureInPictureParams.Builder#setTitle(CharSequence)} should
+         * be prioritized.
+         *
+         * @param subtitle Details about the PIP content.
+         * @return this builder instance
+         */
+        @NonNull
+        public Builder setSubtitle(@Nullable CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Sets whether the built {@link PictureInPictureParams} represents a launch into
+         * picture-in-picture request.
+         *
+         * This property is {@code false} by default.
+         * @param isLaunchIntoPip {@code true} if the built instance represents a launch into
+         *                                 picture-in-picture request
+         * @return this builder instance.
+         */
+        @NonNull
+        Builder setIsLaunchIntoPip(boolean isLaunchIntoPip) {
+            mIsLaunchIntoPip = isLaunchIntoPip;
+            return this;
+        }
+
+        /**
          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
          * the activity in picture-in-picture.
          *
@@ -176,8 +266,9 @@
          */
         public PictureInPictureParams build() {
             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio,
-                    mExpandedAspectRatio, mUserActions,
-                    mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled);
+                    mExpandedAspectRatio, mUserActions, mCloseAction, mSourceRectHint,
+                    mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
+                    mIsLaunchIntoPip);
             return params;
         }
     }
@@ -201,6 +292,12 @@
     private List<RemoteAction> mUserActions;
 
     /**
+     * Action to replace the system close action.
+     */
+    @Nullable
+    private RemoteAction mCloseAction;
+
+    /**
      * The source bounds hint used when entering picture-in-picture, relative to the window bounds.
      * We can use this internally for the transition into picture-in-picture to ensure that a
      * particular source rect is visible throughout the whole transition.
@@ -221,6 +318,25 @@
      */
     private Boolean mSeamlessResizeEnabled;
 
+    /**
+     * Title of the picture-in-picture window to be displayed to the user.
+     */
+    @Nullable
+    private CharSequence mTitle;
+
+    /**
+     * Subtitle for the picture-in-picture window to be displayed to the user.
+     */
+    @Nullable
+    private CharSequence mSubtitle;
+
+    /**
+     * Whether this {@link PictureInPictureParams} represents a launch into
+     * picture-in-picture request.
+     * {@link #isLaunchIntoPip()} defaults to {@code false} is this is not set.
+     */
+    private Boolean mIsLaunchIntoPip;
+
     /** {@hide} */
     PictureInPictureParams() {
     }
@@ -233,6 +349,7 @@
             mUserActions = new ArrayList<>();
             in.readTypedList(mUserActions, RemoteAction.CREATOR);
         }
+        mCloseAction = in.readTypedObject(RemoteAction.CREATOR);
         if (in.readInt() != 0) {
             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
         }
@@ -242,18 +359,32 @@
         if (in.readInt() != 0) {
             mSeamlessResizeEnabled = in.readBoolean();
         }
+        if (in.readInt() != 0) {
+            mTitle = in.readCharSequence();
+        }
+        if (in.readInt() != 0) {
+            mSubtitle = in.readCharSequence();
+        }
+        if (in.readInt() != 0) {
+            mIsLaunchIntoPip = in.readBoolean();
+        }
     }
 
     /** {@hide} */
     PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio,
-            List<RemoteAction> actions, Rect sourceRectHint, Boolean autoEnterEnabled,
-            Boolean seamlessResizeEnabled) {
+            List<RemoteAction> actions, RemoteAction closeAction, Rect sourceRectHint,
+            Boolean autoEnterEnabled, Boolean seamlessResizeEnabled, CharSequence title,
+            CharSequence subtitle, Boolean isLaunchIntoPip) {
         mAspectRatio = aspectRatio;
         mExpandedAspectRatio = expandedAspectRatio;
         mUserActions = actions;
+        mCloseAction = closeAction;
         mSourceRectHint = sourceRectHint;
         mAutoEnterEnabled = autoEnterEnabled;
         mSeamlessResizeEnabled = seamlessResizeEnabled;
+        mTitle = title;
+        mSubtitle = subtitle;
+        mIsLaunchIntoPip = isLaunchIntoPip;
     }
 
     /**
@@ -261,9 +392,10 @@
      * @hide
      */
     public PictureInPictureParams(PictureInPictureParams other) {
-        this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions,
+        this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions, other.mCloseAction,
                 other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
-                other.mAutoEnterEnabled, other.mSeamlessResizeEnabled);
+                other.mAutoEnterEnabled, other.mSeamlessResizeEnabled,
+                other.mTitle, other.mSubtitle, other.mIsLaunchIntoPip);
     }
 
     /**
@@ -281,6 +413,9 @@
         if (otherArgs.hasSetActions()) {
             mUserActions = otherArgs.mUserActions;
         }
+        if (otherArgs.hasSetCloseAction()) {
+            mCloseAction = otherArgs.mCloseAction;
+        }
         if (otherArgs.hasSourceBoundsHint()) {
             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
         }
@@ -290,6 +425,15 @@
         if (otherArgs.mSeamlessResizeEnabled != null) {
             mSeamlessResizeEnabled = otherArgs.mSeamlessResizeEnabled;
         }
+        if (otherArgs.hasSetTitle()) {
+            mTitle = otherArgs.mTitle;
+        }
+        if (otherArgs.hasSetSubtitle()) {
+            mSubtitle = otherArgs.mSubtitle;
+        }
+        if (otherArgs.mIsLaunchIntoPip != null) {
+            mIsLaunchIntoPip = otherArgs.mIsLaunchIntoPip;
+        }
     }
 
     /**
@@ -355,7 +499,26 @@
     }
 
     /**
+     * @return the close action.
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public RemoteAction getCloseAction() {
+        return mCloseAction;
+    }
+
+    /**
+     * @return whether the close action was set.
+     * @hide
+     */
+    public boolean hasSetCloseAction() {
+        return mCloseAction != null;
+    }
+
+    /**
      * Truncates the set of actions to the given {@param size}.
+     *
      * @hide
      */
     public void truncateActions(int size) {
@@ -399,13 +562,58 @@
     }
 
     /**
+     * @return whether a title was set.
+     * @hide
+     */
+    public boolean hasSetTitle() {
+        return mTitle != null;
+    }
+
+    /**
+     * @return title of the pip.
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * @return whether a subtitle was set.
+     * @hide
+     */
+    public boolean hasSetSubtitle() {
+        return mSubtitle != null;
+    }
+
+    /**
+     * @return subtitle of the pip.
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * @return whether this {@link PictureInPictureParams} represents a launch into pip request.
+     * @hide
+     */
+    public boolean isLaunchIntoPip() {
+        return mIsLaunchIntoPip == null ? false : mIsLaunchIntoPip;
+    }
+
+    /**
      * @return True if no parameters are set
      * @hide
      */
     public boolean empty() {
-        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
-                && !hasSetExpandedAspectRatio() && mAutoEnterEnabled != null
-                && mSeamlessResizeEnabled != null;
+        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetCloseAction()
+                && !hasSetAspectRatio() && !hasSetExpandedAspectRatio() && mAutoEnterEnabled == null
+                && mSeamlessResizeEnabled == null && !hasSetTitle()
+                && !hasSetSubtitle() && mIsLaunchIntoPip == null;
     }
 
     @Override
@@ -418,13 +626,18 @@
                 && Objects.equals(mAspectRatio, that.mAspectRatio)
                 && Objects.equals(mExpandedAspectRatio, that.mExpandedAspectRatio)
                 && Objects.equals(mUserActions, that.mUserActions)
-                && Objects.equals(mSourceRectHint, that.mSourceRectHint);
+                && Objects.equals(mCloseAction, that.mCloseAction)
+                && Objects.equals(mSourceRectHint, that.mSourceRectHint)
+                && Objects.equals(mTitle, that.mTitle)
+                && Objects.equals(mSubtitle, that.mSubtitle)
+                && Objects.equals(mIsLaunchIntoPip, that.mIsLaunchIntoPip);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mSourceRectHint,
-                mAutoEnterEnabled, mSeamlessResizeEnabled);
+        return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mCloseAction,
+                mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
+                mIsLaunchIntoPip);
     }
 
     @Override
@@ -442,6 +655,9 @@
         } else {
             out.writeInt(0);
         }
+
+        out.writeTypedObject(mCloseAction, 0);
+
         if (mSourceRectHint != null) {
             out.writeInt(1);
             mSourceRectHint.writeToParcel(out, 0);
@@ -460,6 +676,24 @@
         } else {
             out.writeInt(0);
         }
+        if (mTitle != null) {
+            out.writeInt(1);
+            out.writeCharSequence(mTitle);
+        } else {
+            out.writeInt(0);
+        }
+        if (mSubtitle != null) {
+            out.writeInt(1);
+            out.writeCharSequence(mSubtitle);
+        } else {
+            out.writeInt(0);
+        }
+        if (mIsLaunchIntoPip != null) {
+            out.writeInt(1);
+            out.writeBoolean(mIsLaunchIntoPip);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     private void writeRationalToParcel(Rational rational, Parcel out) {
@@ -486,8 +720,12 @@
                 + " expandedAspectRatio=" + mExpandedAspectRatio
                 + " sourceRectHint=" + getSourceRectHint()
                 + " hasSetActions=" + hasSetActions()
+                + " hasSetCloseAction=" + hasSetCloseAction()
                 + " isAutoPipEnabled=" + isAutoEnterEnabled()
                 + " isSeamlessResizeEnabled=" + isSeamlessResizeEnabled()
+                + " title=" + getTitle()
+                + " subtitle=" + getSubtitle()
+                + " isLaunchIntoPip=" + isLaunchIntoPip()
                 + ")";
     }
 
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 3d2c03d..5c7c73c 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -186,6 +186,18 @@
     public PictureInPictureParams pictureInPictureParams;
 
     /**
+     * @hide
+     */
+    public boolean preferDockBigOverlays;
+
+    /**
+     * The task id of the host Task of the launch-into-pip Activity, i.e., it points to the Task
+     * the launch-into-pip Activity is originated from.
+     * @hide
+     */
+    public int launchIntoPipHostTaskId;
+
+    /**
      * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
      * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
      * {@code null} otherwise.
@@ -379,6 +391,12 @@
     }
 
     /** @hide */
+    @TestApi
+    public boolean getPreferDockBigOverlays() {
+        return preferDockBigOverlays;
+    }
+
+    /** @hide */
     @WindowConfiguration.WindowingMode
     public int getWindowingMode() {
         return configuration.windowConfiguration.getWindowingMode();
@@ -447,6 +465,7 @@
                 && displayAreaFeatureId == that.displayAreaFeatureId
                 && Objects.equals(positionInParent, that.positionInParent)
                 && Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
+                && Objects.equals(preferDockBigOverlays, that.preferDockBigOverlays)
                 && Objects.equals(displayCutoutInsets, that.displayCutoutInsets)
                 && getWindowingMode() == that.getWindowingMode()
                 && Objects.equals(taskDescription, that.taskDescription)
@@ -503,6 +522,8 @@
         token = WindowContainerToken.CREATOR.createFromParcel(source);
         topActivityType = source.readInt();
         pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
+        preferDockBigOverlays = source.readBoolean();
+        launchIntoPipHostTaskId = source.readInt();
         displayCutoutInsets = source.readTypedObject(Rect.CREATOR);
         topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
         isResizeable = source.readBoolean();
@@ -548,6 +569,8 @@
         token.writeToParcel(dest, flags);
         dest.writeInt(topActivityType);
         dest.writeTypedObject(pictureInPictureParams, flags);
+        dest.writeBoolean(preferDockBigOverlays);
+        dest.writeInt(launchIntoPipHostTaskId);
         dest.writeTypedObject(displayCutoutInsets, flags);
         dest.writeTypedObject(topActivityInfo, flags);
         dest.writeBoolean(isResizeable);
@@ -587,6 +610,8 @@
                 + " token=" + token
                 + " topActivityType=" + topActivityType
                 + " pictureInPictureParams=" + pictureInPictureParams
+                + " preferDockBigOverlays=" + preferDockBigOverlays
+                + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId
                 + " displayCutoutSafeInsets=" + displayCutoutInsets
                 + " topActivityInfo=" + topActivityInfo
                 + " launchCookies=" + launchCookies
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index cfaffb1..ecab37d 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -244,7 +244,18 @@
     public static final int MODE_NIGHT_YES = 2;
 
     /**
-     * Granular types for {@link MODE_NIGHT_CUSTOM_TYPE_BEDTIME}
+     * Granular types for {@link #setNightModeCustomType(int)}
+     * @hide
+     */
+    @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
+            MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
+            MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NightModeCustomType {}
+
+    /**
+     * Granular types for {@link #getNightModeCustomType()}
      * @hide
      */
     @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
@@ -253,7 +264,7 @@
             MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface NightModeCustomType {}
+    public @interface NightModeCustomReturnType {}
 
     /**
      * A granular type for {@link #MODE_NIGHT_CUSTOM} which is unknown.
@@ -539,6 +550,8 @@
      * {@code nightModeCustomType}.
      *
      * @param nightModeCustomType
+     * @throws IllegalArgumentException if passed an unsupported type to
+     *         {@code nightModeCustomType}.
      * @hide
      */
     @SystemApi
@@ -562,7 +575,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
-    public int getNightModeCustomType() {
+    public @NightModeCustomReturnType int getNightModeCustomType() {
         if (mService != null) {
             try {
                 return mService.getNightModeCustomType();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9a7093e..55816e4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2523,7 +2523,7 @@
     public @interface UserProvisioningState {}
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Unknown error code returned  for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}.
@@ -2531,10 +2531,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_UNKNOWN_ERROR = -1;
+    public static final int STATUS_UNKNOWN_ERROR = -1;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE} and {@link #ACTION_PROVISION_MANAGED_USER}
@@ -2543,10 +2543,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_OK = 0;
+    public static final int STATUS_OK = 0;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the device already has a
      * device owner.
@@ -2554,10 +2554,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_HAS_DEVICE_OWNER = 1;
+    public static final int STATUS_HAS_DEVICE_OWNER = 1;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user has a profile owner
      *  and for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the profile owner is already set.
@@ -2565,20 +2565,20 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_USER_HAS_PROFILE_OWNER = 2;
+    public static final int STATUS_USER_HAS_PROFILE_OWNER = 2;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} when the user isn't running.
      *
      * @hide
      */
     @SystemApi
-    public static final int CODE_USER_NOT_RUNNING = 3;
+    public static final int STATUS_USER_NOT_RUNNING = 3;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the device has already been
      * setup and for {@link #ACTION_PROVISION_MANAGED_USER} if the user has already been setup.
@@ -2586,7 +2586,7 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_USER_SETUP_COMPLETED = 4;
+    public static final int STATUS_USER_SETUP_COMPLETED = 4;
 
     /**
      * Code used to indicate that the device also has a user other than the system user.
@@ -2594,7 +2594,7 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_NONSYSTEM_USER_EXISTS = 5;
+    public static final int STATUS_NONSYSTEM_USER_EXISTS = 5;
 
     /**
      * Code used to indicate that device has an account that prevents provisioning.
@@ -2602,20 +2602,20 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
+    public static final int STATUS_ACCOUNTS_NOT_EMPTY = 6;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} if the user is not a system user.
      *
      * @hide
      */
     @SystemApi
-    public static final int CODE_NOT_SYSTEM_USER = 7;
+    public static final int STATUS_NOT_SYSTEM_USER = 7;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
      * {@link #ACTION_PROVISION_MANAGED_USER} when the device is a watch and is already paired.
@@ -2623,10 +2623,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_HAS_PAIRED = 8;
+    public static final int STATUS_HAS_PAIRED = 8;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} and
      * {@link #ACTION_PROVISION_MANAGED_USER} on devices which do not support managed users.
@@ -2635,10 +2635,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_MANAGED_USERS_NOT_SUPPORTED = 9;
+    public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_USER} if the user is a system user and
      * for {@link #ACTION_PROVISION_MANAGED_DEVICE} on devices running headless system user mode
@@ -2647,10 +2647,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_SYSTEM_USER = 10;
+    public static final int STATUS_SYSTEM_USER = 10;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the user cannot have more
      * managed profiles.
@@ -2658,19 +2658,10 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11;
+    public static final int STATUS_CANNOT_ADD_MANAGED_PROFILE = 11;
 
     /**
-     * TODO (b/137101239): clean up split system user codes
-     *
-     * @hide
-     * @deprecated not used anymore but can't be removed since it's a @TestApi.
-     **/
-    @Deprecated
-    public static final int CODE_NOT_SYSTEM_USER_SPLIT = 12;
-
-    /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE},
      * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support device
@@ -2679,21 +2670,21 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13;
+    public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13;
 
     /**
      * TODO (b/137101239): clean up split system user codes
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * @hide
      * @deprecated not used anymore but can't be removed since it's a @TestApi.
      */
     @Deprecated
     @TestApi
-    public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
+    public static final int STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
 
     /**
-     * Result code for {@link #checkProvisioningPreCondition}.
+     * Result code for {@link #checkProvisioningPrecondition}.
      *
      * <p>Returned for {@link #ACTION_PROVISION_MANAGED_DEVICE} and
      * {@link #ACTION_PROVISION_MANAGED_PROFILE} on devices which do not support provisioning.
@@ -2701,24 +2692,24 @@
      * @hide
      */
     @SystemApi
-    public static final int CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
+    public static final int STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS = 15;
 
     /**
-     * Result codes for {@link #checkProvisioningPreCondition} indicating all the provisioning pre
+     * Result codes for {@link #checkProvisioningPrecondition} indicating all the provisioning pre
      * conditions.
      *
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "CODE_" }, value = {
-            CODE_UNKNOWN_ERROR, CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER,
-            CODE_USER_NOT_RUNNING, CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
-            CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
-            CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
-            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
-            CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN_ERROR, STATUS_OK, STATUS_HAS_DEVICE_OWNER, STATUS_USER_HAS_PROFILE_OWNER,
+            STATUS_USER_NOT_RUNNING, STATUS_USER_SETUP_COMPLETED, STATUS_NOT_SYSTEM_USER,
+            STATUS_HAS_PAIRED, STATUS_MANAGED_USERS_NOT_SUPPORTED, STATUS_SYSTEM_USER,
+            STATUS_CANNOT_ADD_MANAGED_PROFILE, STATUS_DEVICE_ADMIN_NOT_SUPPORTED,
+            STATUS_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER,
+            STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS
     })
-    public @interface ProvisioningPreCondition {}
+    public @interface ProvisioningPrecondition {}
 
     /**
      * Disable all configurable SystemUI features during LockTask mode. This includes,
@@ -6070,7 +6061,7 @@
      * organization-owned managed profile.
      *
      * <p>The caller must hold the
-     * {@link android.Manifest.permission#SEND_LOST_MODE_LOCATION_UPDATES} permission.
+     * {@link android.Manifest.permission#TRIGGER_LOST_MODE} permission.
      *
      * <p> Not for use by third-party applications.
      *
@@ -6080,7 +6071,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES)
+    @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE)
     public void sendLostModeLocationUpdate(@NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<Boolean> callback) {
         throwIfParentInstance("sendLostModeLocationUpdate");
@@ -11996,15 +11987,16 @@
      *        {@link #ACTION_PROVISION_MANAGED_PROFILE}
      * @param packageName The package of the component that would be set as device, user, or profile
      *        owner.
-     * @return A {@link ProvisioningPreCondition} value indicating whether provisioning is allowed.
+     * @return An int constant value indicating whether provisioning is allowed.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
-    public @ProvisioningPreCondition int checkProvisioningPreCondition(
+    @ProvisioningPrecondition
+    public int checkProvisioningPrecondition(
             @NonNull String action, @NonNull String packageName) {
         try {
-            return mService.checkProvisioningPreCondition(action, packageName);
+            return mService.checkProvisioningPrecondition(action, packageName);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -14689,7 +14681,7 @@
      * {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
      * owner.
      *
-     * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+     * <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
      * before calling this method.
      *
      * @param provisioningParams Params required to provision a managed profile,
@@ -14733,7 +14725,7 @@
      * Provisions a managed device and sets the {@code deviceAdminComponentName} as the device
      * owner.
      *
-     * <p>The method {@link #checkProvisioningPreCondition} must be returning {@link #CODE_OK}
+     * <p>The method {@link #checkProvisioningPrecondition} must be returning {@link #STATUS_OK}
      * before calling this method.
      *
      * @param provisioningParams Params required to provision a fully managed device,
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 7f2e5fd..042e407 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -317,6 +317,8 @@
  */
 public final class DevicePolicyResources {
 
+    private DevicePolicyResources() {}
+
     /**
      * Resource identifiers used to update device management-related system drawable resources.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0b9d51f..9d28dde 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -373,7 +373,7 @@
             String permission, int grantState, in RemoteCallback resultReceiver);
     int getPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, String permission);
     boolean isProvisioningAllowed(String action, String packageName);
-    int checkProvisioningPreCondition(String action, String packageName);
+    int checkProvisioningPrecondition(String action, String packageName);
     void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList);
     List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage);
     boolean isManagedProfile(in ComponentName admin);
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
index 57a2c50..a457b5f 100644
--- a/core/java/android/app/admin/ProvisioningException.java
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -46,7 +46,7 @@
     /**
      * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
      * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
-     * Indicates the call to {@link DevicePolicyManager#checkProvisioningPreCondition} returned an
+     * Indicates the call to {@link DevicePolicyManager#checkProvisioningPrecondition} returned an
      * error code.
      */
     public static final int ERROR_PRE_CONDITION_FAILED = 1;
diff --git a/core/java/android/app/assist/OWNERS b/core/java/android/app/assist/OWNERS
index 46b5ea0..e857c72 100644
--- a/core/java/android/app/assist/OWNERS
+++ b/core/java/android/app/assist/OWNERS
@@ -1,7 +1,5 @@
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
 augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+joannechung@google.com
+markpun@google.com
+lpeter@google.com
+tymtsai@google.com
diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java
index ef66c0c..bf78325 100644
--- a/core/java/android/app/cloudsearch/SearchRequest.java
+++ b/core/java/android/app/cloudsearch/SearchRequest.java
@@ -83,11 +83,13 @@
      *  presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or
      *  "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit.
      */
-    public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION";
+    public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION =
+            "android.app.cloudsearch.IS_PRESUBMIT_SUGGESTION";
     /** The target search provider list of package names(separated by ;), String value expected.
      * If this is not provided or its value is empty, then no filter will be applied.
      */
-    public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER";
+    public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER =
+            "android.app.cloudsearch.SEARCH_PROVIDER_FILTER";
 
     @NonNull
     private Bundle mSearchConstraints;
@@ -98,7 +100,7 @@
      *
      */
     @NonNull
-    private String mSource;
+    private String mCallerPackageName;
 
     private SearchRequest(Parcel in) {
         this.mQuery = in.readString();
@@ -107,17 +109,17 @@
         this.mMaxLatencyMillis = in.readFloat();
         this.mSearchConstraints = in.readBundle();
         this.mId = in.readString();
-        this.mSource = in.readString();
+        this.mCallerPackageName = in.readString();
     }
 
     private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis,
-            Bundle searchConstraints, String source) {
+            Bundle searchConstraints, String callerPackageName) {
         mQuery = query;
         mResultOffset = resultOffset;
         mResultNumber = resultNumber;
         mMaxLatencyMillis = maxLatencyMillis;
         mSearchConstraints = searchConstraints;
-        mSource = source;
+        mCallerPackageName = callerPackageName;
     }
 
     /** Returns the original query. */
@@ -149,8 +151,8 @@
 
     /** Gets the caller's package name. */
     @NonNull
-    public String getSource() {
-        return mSource;
+    public String getCallerPackageName() {
+        return mCallerPackageName;
     }
 
     /** Returns the search request id, which is used to identify the request. */
@@ -167,8 +169,8 @@
      *
      * @hide
      */
-    public void setSource(@NonNull String source) {
-        this.mSource = source;
+    public void setCallerPackageName(@NonNull String callerPackageName) {
+        this.mCallerPackageName = callerPackageName;
     }
 
     private SearchRequest(Builder b) {
@@ -177,7 +179,7 @@
         mResultNumber = b.mResultNumber;
         mMaxLatencyMillis = b.mMaxLatencyMillis;
         mSearchConstraints = requireNonNull(b.mSearchConstraints);
-        mSource = requireNonNull(b.mSource);
+        mCallerPackageName = requireNonNull(b.mCallerPackageName);
     }
 
     /**
@@ -205,7 +207,7 @@
         dest.writeFloat(this.mMaxLatencyMillis);
         dest.writeBundle(this.mSearchConstraints);
         dest.writeString(getRequestId());
-        dest.writeString(this.mSource);
+        dest.writeString(this.mCallerPackageName);
     }
 
     @Override
@@ -229,7 +231,7 @@
                 && mResultNumber == that.mResultNumber
                 && mMaxLatencyMillis == that.mMaxLatencyMillis
                 && Objects.equals(mSearchConstraints, that.mSearchConstraints)
-                && Objects.equals(mSource, that.mSource);
+                && Objects.equals(mCallerPackageName, that.mCallerPackageName);
     }
 
     @Override
@@ -244,14 +246,15 @@
         }
 
         return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;"
-                        + "is_presubmit:%b;search_provider:%s;source:%s}", mQuery, mResultOffset,
-                mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider, mSource);
+                        + "is_presubmit:%b;search_provider:%s;callerPackageName:%s}", mQuery,
+                mResultOffset, mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider,
+                mCallerPackageName);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
-                mSearchConstraints, mSource);
+                mSearchConstraints, mCallerPackageName);
     }
 
     /**
@@ -266,7 +269,7 @@
         private int mResultNumber;
         private float mMaxLatencyMillis;
         private Bundle mSearchConstraints;
-        private String mSource;
+        private String mCallerPackageName;
 
         /**
          *
@@ -282,7 +285,7 @@
             mResultNumber = 10;
             mMaxLatencyMillis = 200;
             mSearchConstraints = Bundle.EMPTY;
-            mSource = "DEFAULT_CALLER";
+            mCallerPackageName = "DEFAULT_CALLER";
         }
 
         /** Sets the input query. */
@@ -327,8 +330,8 @@
          */
         @NonNull
         @TestApi
-        public Builder setSource(@NonNull String source) {
-            this.mSource = source;
+        public Builder setCallerPackageName(@NonNull String callerPackageName) {
+            this.mCallerPackageName = callerPackageName;
             return this;
         }
 
@@ -341,7 +344,7 @@
             }
 
             return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
-                               mSearchConstraints, mSource);
+                               mSearchConstraints, mCallerPackageName);
         }
     }
 }
diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java
index 3403ab0..1ca01d4 100644
--- a/core/java/android/app/cloudsearch/SearchResult.java
+++ b/core/java/android/app/cloudsearch/SearchResult.java
@@ -71,51 +71,68 @@
             EXTRAINFO_APP_BADGES,
             EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING,
             EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING,
+            EXTRAINFO_APP_CARD_ACTION,
+            EXTRAINFO_INSTALL_BUTTON_ACTION,
             EXTRAINFO_WEB_URL,
             EXTRAINFO_WEB_ICON})
     public @interface SearchResultExtraInfoKey {}
     /** This App developer website's domain URL, String value expected. */
-    public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL";
+    public static final String EXTRAINFO_APP_DOMAIN_URL = "android.app.cloudsearch.APP_DOMAIN_URL";
     /** This App icon, android.graphics.drawable.Icon expected. */
-    public static final String EXTRAINFO_APP_ICON = "APP_ICON";
+    public static final String EXTRAINFO_APP_ICON = "android.app.cloudsearch.APP_ICON";
     /** This App developer's name, String value expected. */
-    public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME";
+    public static final String EXTRAINFO_APP_DEVELOPER_NAME =
+            "android.app.cloudsearch.APP_DEVELOPER_NAME";
     /** This App's pkg size in bytes, Double value expected. */
-    public static final String EXTRAINFO_APP_SIZE_BYTES = "APP_SIZE_BYTES";
+    public static final String EXTRAINFO_APP_SIZE_BYTES = "android.app.cloudsearch.APP_SIZE_BYTES";
     /** This App developer's name, Double value expected. */
-    public static final String EXTRAINFO_APP_STAR_RATING = "APP_STAR_RATING";
+    public static final String EXTRAINFO_APP_STAR_RATING =
+            "android.app.cloudsearch.APP_STAR_RATING";
     /** This App's IARC rating, String value expected.
      * IARC (International Age Rating Coalition) is partnered globally with major
      * content rating organizations to provide a centralized and one-stop-shop for
      * rating content on a global scale.
      */
-    public static final String EXTRAINFO_APP_IARC = "APP_IARC";
+    public static final String EXTRAINFO_APP_IARC = "android.app.cloudsearch.APP_IARC";
     /** This App's review count, Double value expected. */
-    public static final String EXTRAINFO_APP_REVIEW_COUNT = "APP_REVIEW_COUNT";
+    public static final String EXTRAINFO_APP_REVIEW_COUNT =
+            "android.app.cloudsearch.APP_REVIEW_COUNT";
     /** If this App contains the Ads Disclaimer, Boolean value expected. */
     public static final String EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER =
-            "APP_CONTAINS_ADS_DISCLAIMER";
+            "android.app.cloudsearch.APP_CONTAINS_ADS_DISCLAIMER";
     /** If this App contains the IAP Disclaimer, Boolean value expected. */
     public static final String EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER =
-            "APP_CONTAINS_IAP_DISCLAIMER";
+            "android.app.cloudsearch.APP_CONTAINS_IAP_DISCLAIMER";
     /** This App's short description, String value expected. */
-    public static final String EXTRAINFO_SHORT_DESCRIPTION = "SHORT_DESCRIPTION";
+    public static final String EXTRAINFO_SHORT_DESCRIPTION =
+            "android.app.cloudsearch.SHORT_DESCRIPTION";
     /** This App's long description, String value expected. */
-    public static final String EXTRAINFO_LONG_DESCRIPTION = "LONG_DESCRIPTION";
-    /** This App's screenshots, List<ImageLoadingBundle> value expected. */
-    public static final String EXTRAINFO_SCREENSHOTS = "SCREENSHOTS";
-    /** Editor's choices for this App, ArrayList<String> value expected. */
-    public static final String EXTRAINFO_APP_BADGES = "APP_BADGES";
+    public static final String EXTRAINFO_LONG_DESCRIPTION =
+            "android.app.cloudsearch.LONG_DESCRIPTION";
+    /** This App's screenshots, {@code List<ImageLoadingBundle>} value expected. */
+    public static final String EXTRAINFO_SCREENSHOTS = "android.app.cloudsearch.SCREENSHOTS";
+    /** Editor's choices for this App, {@code ArrayList<String>} value expected. */
+    public static final String EXTRAINFO_APP_BADGES = "android.app.cloudsearch.APP_BADGES";
     /** Pre-registration game's action button text, String value expected. */
     @SuppressLint("IntentName")
-    public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING = "ACTION_BUTTON_TEXT";
+    public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING =
+            "android.app.cloudsearch.ACTION_BUTTON_TEXT";
     /** Pre-registration game's action button image, ImageLoadingBundle value expected. */
     @SuppressLint("IntentName")
-    public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE";
+    public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING =
+            "android.app.cloudsearch.ACTION_BUTTON_IMAGE";
+    /** Intent for tapping the app card, PendingIntent expected. */
+    @SuppressLint("IntentName")
+    public static final String EXTRAINFO_APP_CARD_ACTION =
+            "android.app.cloudsearch.APP_CARD_ACTION";
+    /** Intent for tapping the install button, PendingIntent expected. */
+    @SuppressLint("IntentName")
+    public static final String EXTRAINFO_INSTALL_BUTTON_ACTION =
+            "android.app.cloudsearch.INSTALL_BUTTON_ACTION";
     /** Web content's URL, String value expected. */
-    public static final String EXTRAINFO_WEB_URL = "WEB_URL";
+    public static final String EXTRAINFO_WEB_URL = "android.app.cloudsearch.WEB_URL";
     /** Web content's domain icon, android.graphics.drawable.Icon expected. */
-    public static final String EXTRAINFO_WEB_ICON = "WEB_ICON";
+    public static final String EXTRAINFO_WEB_ICON = "android.app.cloudsearch.WEB_ICON";
 
     @NonNull
     private Bundle mExtraInfos;
diff --git a/core/java/android/app/contentsuggestions/OWNERS b/core/java/android/app/contentsuggestions/OWNERS
index 482abb2..cf54c2a 100644
--- a/core/java/android/app/contentsuggestions/OWNERS
+++ b/core/java/android/app/contentsuggestions/OWNERS
@@ -1,9 +1,7 @@
 # Bug component: 643919
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
 augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+joannechung@google.com
+markpun@google.com
+lpeter@google.com
+tymtsai@google.com
diff --git a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
index a07af68..584b176 100644
--- a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java
@@ -33,6 +33,9 @@
  *     <li> title_text (may contain a start drawable) </li>
  *     <li> subtitle_text (may contain a start drawable) . supplemental_subtitle_text (may
  *     contain a start drawable) </li>
+ *
+ *     <li> supplemental_text (contain a start drawable) . do_not_disturb_view </li>
+ *     Or
  *     <li> next_alarm_text (contain a start drawable) + supplemental_alarm_text .
  *     do_not_disturb_view </li>
  * </ul>
@@ -77,6 +80,14 @@
     private final TapAction mPrimaryTapAction;
 
     /**
+     * Primary logging info for the entire card. This will only be used when rendering a sub card
+     * within the base card. For the base card itself, BcSmartspaceCardLoggingInfo should be used,
+     * which has the display-specific info (e.g. display surface).
+     */
+    @Nullable
+    private final SubItemLoggingInfo mPrimaryLoggingInfo;
+
+    /**
      * Supplemental subtitle text and icon are shown at the second row following the subtitle text.
      * Mainly used for weather info on non-weather card.
      */
@@ -87,19 +98,47 @@
     private final Icon mSupplementalSubtitleIcon;
 
     /**
-     * Tap action for the supplemental subtitle's text and icon. Will use the primary tap action if
+     * Tap action for the supplemental subtitle's text and icon. Uses the primary tap action if
      * not being set.
      */
     @Nullable
     private final TapAction mSupplementalSubtitleTapAction;
 
     /**
+     * Logging info for the supplemental subtitle's are. Uses the primary logging info if not being
+     * set.
+     */
+    @Nullable
+    private final SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
+
+    @Nullable
+    private final Text mSupplementalText;
+
+    @Nullable
+    private final Icon mSupplementalIcon;
+
+    @Nullable
+    private final TapAction mSupplementalTapAction;
+
+    /**
+     * Logging info for the supplemental line. Uses the primary logging info if not being set.
+     */
+    @Nullable
+    private final SubItemLoggingInfo mSupplementalLoggingInfo;
+
+    /**
      * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next
      * alarm".
      */
     @Nullable
     private final Text mSupplementalAlarmText;
 
+    /**
+     * The layout weight info for the card, which indicates how much space it should occupy on the
+     * screen. Default weight is 0.
+     */
+    private final int mLayoutWeight;
+
     BaseTemplateData(@NonNull Parcel in) {
         mTemplateType = in.readInt();
         mTitleText = in.readTypedObject(Text.CREATOR);
@@ -107,10 +146,17 @@
         mSubtitleText = in.readTypedObject(Text.CREATOR);
         mSubtitleIcon = in.readTypedObject(Icon.CREATOR);
         mPrimaryTapAction = in.readTypedObject(TapAction.CREATOR);
+        mPrimaryLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
         mSupplementalSubtitleText = in.readTypedObject(Text.CREATOR);
         mSupplementalSubtitleIcon = in.readTypedObject(Icon.CREATOR);
         mSupplementalSubtitleTapAction = in.readTypedObject(TapAction.CREATOR);
+        mSupplementalSubtitleLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
+        mSupplementalText = in.readTypedObject(Text.CREATOR);
+        mSupplementalIcon = in.readTypedObject(Icon.CREATOR);
+        mSupplementalTapAction = in.readTypedObject(TapAction.CREATOR);
+        mSupplementalLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR);
         mSupplementalAlarmText = in.readTypedObject(Text.CREATOR);
+        mLayoutWeight = in.readInt();
     }
 
     /**
@@ -123,20 +169,34 @@
             @Nullable Text subtitleText,
             @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
-            @Nullable Text supplementalAlarmText) {
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
+            @Nullable Text supplementalAlarmText,
+            int layoutWeight) {
         mTemplateType = templateType;
         mTitleText = titleText;
         mTitleIcon = titleIcon;
         mSubtitleText = subtitleText;
         mSubtitleIcon = subtitleIcon;
         mPrimaryTapAction = primaryTapAction;
+        mPrimaryLoggingInfo = primaryLoggingInfo;
         mSupplementalSubtitleText = supplementalSubtitleText;
         mSupplementalSubtitleIcon = supplementalSubtitleIcon;
         mSupplementalSubtitleTapAction = supplementalSubtitleTapAction;
+        mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
+        mSupplementalText = supplementalText;
+        mSupplementalIcon = supplementalIcon;
+        mSupplementalTapAction = supplementalTapAction;
+        mSupplementalLoggingInfo = supplementalLoggingInfo;
         mSupplementalAlarmText = supplementalAlarmText;
+        mLayoutWeight = layoutWeight;
     }
 
     /** Returns the template type. By default is UNDEFINED. */
@@ -175,6 +235,12 @@
         return mPrimaryTapAction;
     }
 
+    /** Returns the card's primary logging info. */
+    @Nullable
+    public SubItemLoggingInfo getPrimaryLoggingInfo() {
+        return mPrimaryLoggingInfo;
+    }
+
     /** Returns the supplemental subtitle's text. */
     @Nullable
     public Text getSupplementalSubtitleText() {
@@ -193,12 +259,47 @@
         return mSupplementalSubtitleTapAction;
     }
 
+    /** Returns the card's supplemental title's logging info. */
+    @Nullable
+    public SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
+        return mSupplementalSubtitleLoggingInfo;
+    }
+
+    /** Returns the supplemental text. */
+    @Nullable
+    public Text getSupplementalText() {
+        return mSupplementalText;
+    }
+
+    /** Returns the supplemental icon. */
+    @Nullable
+    public Icon getSupplementalIcon() {
+        return mSupplementalIcon;
+    }
+
+    /** Returns the supplemental line's tap action. Can be null if not being set. */
+    @Nullable
+    public TapAction getSupplementalTapAction() {
+        return mSupplementalTapAction;
+    }
+
+    /** Returns the card's supplemental line logging info. */
+    @Nullable
+    public SubItemLoggingInfo getSupplementalLoggingInfo() {
+        return mSupplementalLoggingInfo;
+    }
+
     /** Returns the supplemental alarm text. */
     @Nullable
     public Text getSupplementalAlarmText() {
         return mSupplementalAlarmText;
     }
 
+    /** Returns the card layout weight info. Default weight is 0. */
+    public int getLayoutWeight() {
+        return mLayoutWeight;
+    }
+
     /**
      * @see Parcelable.Creator
      */
@@ -229,10 +330,17 @@
         out.writeTypedObject(mSubtitleText, flags);
         out.writeTypedObject(mSubtitleIcon, flags);
         out.writeTypedObject(mPrimaryTapAction, flags);
+        out.writeTypedObject(mPrimaryLoggingInfo, flags);
         out.writeTypedObject(mSupplementalSubtitleText, flags);
         out.writeTypedObject(mSupplementalSubtitleIcon, flags);
         out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
+        out.writeTypedObject(mSupplementalSubtitleLoggingInfo, flags);
+        out.writeTypedObject(mSupplementalText, flags);
+        out.writeTypedObject(mSupplementalIcon, flags);
+        out.writeTypedObject(mSupplementalTapAction, flags);
+        out.writeTypedObject(mSupplementalLoggingInfo, flags);
         out.writeTypedObject(mSupplementalAlarmText, flags);
+        out.writeInt(mLayoutWeight);
     }
 
     @Override
@@ -246,19 +354,31 @@
                 && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
                 && Objects.equals(mSubtitleIcon, that.mSubtitleIcon)
                 && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
+                && Objects.equals(mPrimaryLoggingInfo, that.mPrimaryLoggingInfo)
                 && SmartspaceUtils.isEqual(mSupplementalSubtitleText,
                 that.mSupplementalSubtitleText)
                 && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon)
                 && Objects.equals(mSupplementalSubtitleTapAction,
                 that.mSupplementalSubtitleTapAction)
-                && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText);
+                && Objects.equals(mSupplementalSubtitleLoggingInfo,
+                that.mSupplementalSubtitleLoggingInfo)
+                && SmartspaceUtils.isEqual(mSupplementalText,
+                that.mSupplementalText)
+                && Objects.equals(mSupplementalIcon, that.mSupplementalIcon)
+                && Objects.equals(mSupplementalTapAction, that.mSupplementalTapAction)
+                && Objects.equals(mSupplementalLoggingInfo, that.mSupplementalLoggingInfo)
+                && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText)
+                && mLayoutWeight == that.mLayoutWeight;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon,
-                mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon,
-                mSupplementalSubtitleTapAction, mSupplementalAlarmText);
+                mPrimaryTapAction, mPrimaryLoggingInfo, mSupplementalSubtitleText,
+                mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
+                mSupplementalSubtitleLoggingInfo,
+                mSupplementalText, mSupplementalIcon, mSupplementalTapAction,
+                mSupplementalLoggingInfo, mSupplementalAlarmText, mLayoutWeight);
     }
 
     @Override
@@ -270,10 +390,17 @@
                 + ", mSubtitleText=" + mSubtitleText
                 + ", mSubTitleIcon=" + mSubtitleIcon
                 + ", mPrimaryTapAction=" + mPrimaryTapAction
+                + ", mPrimaryLoggingInfo=" + mPrimaryLoggingInfo
                 + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
                 + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
                 + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction
+                + ", mSupplementalSubtitleLoggingInfo=" + mSupplementalSubtitleLoggingInfo
+                + ", mSupplementalText=" + mSupplementalText
+                + ", mSupplementalIcon=" + mSupplementalIcon
+                + ", mSupplementalTapAction=" + mSupplementalTapAction
+                + ", mSupplementalLoggingInfo=" + mSupplementalLoggingInfo
                 + ", mSupplementalAlarmText=" + mSupplementalAlarmText
+                + ", mLayoutWeight=" + mLayoutWeight
                 + '}';
     }
 
@@ -292,18 +419,26 @@
         private Text mSubtitleText;
         private Icon mSubtitleIcon;
         private TapAction mPrimaryTapAction;
+        private SubItemLoggingInfo mPrimaryLoggingInfo;
         private Text mSupplementalSubtitleText;
         private Icon mSupplementalSubtitleIcon;
         private TapAction mSupplementalSubtitleTapAction;
+        private SubItemLoggingInfo mSupplementalSubtitleLoggingInfo;
+        private Text mSupplementalText;
+        private Icon mSupplementalIcon;
+        private TapAction mSupplementalTapAction;
+        private SubItemLoggingInfo mSupplementalLoggingInfo;
         private Text mSupplementalAlarmText;
+        private int mLayoutWeight;
 
         /**
-         * A builder for {@link BaseTemplateData}.
+         * A builder for {@link BaseTemplateData}. By default sets the layout weight to be 0.
          *
          * @param templateType the {@link UiTemplateType} of this template data.
          */
         public Builder(@UiTemplateType int templateType) {
             mTemplateType = templateType;
+            mLayoutWeight = 0;
         }
 
         /** Should ONLY be used by the subclasses */
@@ -351,6 +486,13 @@
         /** Should ONLY be used by the subclasses */
         @Nullable
         @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getPrimaryLoggingInfo() {
+            return mPrimaryLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
         Text getSupplementalSubtitleText() {
             return mSupplementalSubtitleText;
         }
@@ -372,10 +514,51 @@
         /** Should ONLY be used by the subclasses */
         @Nullable
         @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() {
+            return mSupplementalSubtitleLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        Text getSupplementalText() {
+            return mSupplementalText;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        Icon getSupplementalIcon() {
+            return mSupplementalIcon;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        TapAction getSupplementalTapAction() {
+            return mSupplementalTapAction;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
+        SubItemLoggingInfo getSupplementalLoggingInfo() {
+            return mSupplementalLoggingInfo;
+        }
+
+        /** Should ONLY be used by the subclasses */
+        @Nullable
+        @SuppressLint("GetterOnBuilder")
         Text getSupplementalAlarmText() {
             return mSupplementalAlarmText;
         }
 
+        /** Should ONLY be used by the subclasses */
+        @SuppressLint("GetterOnBuilder")
+        int getLayoutWeight() {
+            return mLayoutWeight;
+        }
+
         /**
          * Sets the card title.
          */
@@ -422,6 +605,15 @@
         }
 
         /**
+         * Sets the card primary logging info.
+         */
+        @NonNull
+        public Builder setPrimaryLoggingInfo(@NonNull SubItemLoggingInfo primaryLoggingInfo) {
+            mPrimaryLoggingInfo = primaryLoggingInfo;
+            return this;
+        }
+
+        /**
          * Sets the supplemental subtitle text.
          */
         @NonNull
@@ -443,8 +635,7 @@
 
         /**
          * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not
-         * being
-         * set.
+         * being set.
          */
         @NonNull
         public Builder setSupplementalSubtitleTapAction(
@@ -454,6 +645,54 @@
         }
 
         /**
+         * Sets the card supplemental title's logging info.
+         */
+        @NonNull
+        public Builder setSupplementalSubtitleLoggingInfo(
+                @NonNull SubItemLoggingInfo supplementalSubtitleLoggingInfo) {
+            mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental text.
+         */
+        @NonNull
+        public Builder setSupplementalText(@NonNull Text supplementalText) {
+            mSupplementalText = supplementalText;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental icon.
+         */
+        @NonNull
+        public Builder setSupplementalIcon(@NonNull Icon supplementalIcon) {
+            mSupplementalIcon = supplementalIcon;
+            return this;
+        }
+
+        /**
+         * Sets the supplemental line tap action. {@code mPrimaryTapAction} will be used if not
+         * being set.
+         */
+        @NonNull
+        public Builder setSupplementalTapAction(@NonNull TapAction supplementalTapAction) {
+            mSupplementalTapAction = supplementalTapAction;
+            return this;
+        }
+
+        /**
+         * Sets the card supplemental line's logging info.
+         */
+        @NonNull
+        public Builder setSupplementalLoggingInfo(
+                @NonNull SubItemLoggingInfo supplementalLoggingInfo) {
+            mSupplementalLoggingInfo = supplementalLoggingInfo;
+            return this;
+        }
+
+        /**
          * Sets the supplemental alarm text.
          */
         @NonNull
@@ -463,14 +702,136 @@
         }
 
         /**
+         * Sets the layout weight.
+         */
+        @NonNull
+        public Builder setLayoutWeight(int layoutWeight) {
+            mLayoutWeight = layoutWeight;
+            return this;
+        }
+
+        /**
          * Builds a new SmartspaceDefaultUiTemplateData instance.
          */
         @NonNull
         public BaseTemplateData build() {
             return new BaseTemplateData(mTemplateType, mTitleText, mTitleIcon,
-                    mSubtitleText, mSubtitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
-                    mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
-                    mSupplementalAlarmText);
+                    mSubtitleText, mSubtitleIcon, mPrimaryTapAction,
+                    mPrimaryLoggingInfo,
+                    mSupplementalSubtitleText, mSupplementalSubtitleIcon,
+                    mSupplementalSubtitleTapAction, mSupplementalSubtitleLoggingInfo,
+                    mSupplementalText, mSupplementalIcon,
+                    mSupplementalTapAction, mSupplementalLoggingInfo,
+                    mSupplementalAlarmText, mLayoutWeight);
+        }
+    }
+
+    /**
+     * Holds all the logging info needed for a sub item within the base card. For example, the
+     * supplemental-subtitle part should have its own logging info.
+     */
+    public static final class SubItemLoggingInfo implements Parcelable {
+
+        /** A unique instance id for the sub item. */
+        private final int mInstanceId;
+
+        /** The feature type for this sub item. */
+        private final int mFeatureType;
+
+        SubItemLoggingInfo(@NonNull Parcel in) {
+            mInstanceId = in.readInt();
+            mFeatureType = in.readInt();
+        }
+
+        private SubItemLoggingInfo(int instanceId, int featureType) {
+            mInstanceId = instanceId;
+            mFeatureType = featureType;
+        }
+
+        public int getInstanceId() {
+            return mInstanceId;
+        }
+
+        public int getFeatureType() {
+            return mFeatureType;
+        }
+
+        /**
+         * @see Parcelable.Creator
+         */
+        @NonNull
+        public static final Creator<SubItemLoggingInfo> CREATOR =
+                new Creator<SubItemLoggingInfo>() {
+                    @Override
+                    public SubItemLoggingInfo createFromParcel(Parcel in) {
+                        return new SubItemLoggingInfo(in);
+                    }
+
+                    @Override
+                    public SubItemLoggingInfo[] newArray(int size) {
+                        return new SubItemLoggingInfo[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(mInstanceId);
+            out.writeInt(mFeatureType);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SubItemLoggingInfo)) return false;
+            SubItemLoggingInfo that = (SubItemLoggingInfo) o;
+            return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mInstanceId, mFeatureType);
+        }
+
+        @Override
+        public String toString() {
+            return "SubItemLoggingInfo{"
+                    + "mInstanceId=" + mInstanceId
+                    + ", mFeatureType=" + mFeatureType
+                    + '}';
+        }
+
+        /**
+         * A builder for {@link SubItemLoggingInfo} object.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final class Builder {
+
+            private final int mInstanceId;
+            private final int mFeatureType;
+
+            /**
+             * A builder for {@link SubItemLoggingInfo}.
+             *
+             * @param instanceId  A unique instance id for the sub item
+             * @param featureType The feature type for this sub item
+             */
+            public Builder(int instanceId, int featureType) {
+                mInstanceId = instanceId;
+                mFeatureType = featureType;
+            }
+
+            /** Builds a new {@link SubItemLoggingInfo} instance. */
+            @NonNull
+            public SubItemLoggingInfo build() {
+                return new SubItemLoggingInfo(mInstanceId, mFeatureType);
+            }
         }
     }
 }
diff --git a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
index feb1c34..fbdb7be 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java
@@ -59,17 +59,29 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<CarouselItem> carouselItems,
             @Nullable TapAction carouselAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mCarouselItems = carouselItems;
         mCarouselAction = carouselAction;
     }
@@ -179,10 +191,14 @@
             }
 
             return new CarouselTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mCarouselItems,
-                    mCarouselAction);
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mCarouselItems, mCarouselAction);
         }
     }
 
diff --git a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
index 13091e2..1d13066 100644
--- a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java
@@ -54,16 +54,27 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<BaseTemplateData> combinedCardDataList) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
         mCombinedCardDataList = combinedCardDataList;
     }
 
@@ -151,9 +162,13 @@
                 throw new IllegalStateException("Please assign a value to all @NonNull args.");
             }
             return new CombinedCardsTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
                     mCombinedCardDataList);
         }
     }
diff --git a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
index eb56e93..19177df 100644
--- a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java
@@ -69,21 +69,33 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @Nullable Text headToHeadTitle,
             @Nullable Icon headToHeadFirstCompetitorIcon,
             @Nullable Icon headToHeadSecondCompetitorIcon,
             @Nullable Text headToHeadFirstCompetitorText,
             @Nullable Text headToHeadSecondCompetitorText,
             @Nullable TapAction headToHeadAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mHeadToHeadTitle = headToHeadTitle;
         mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon;
         mHeadToHeadSecondCompetitorIcon = headToHeadSecondCompetitorIcon;
@@ -285,9 +297,13 @@
         @NonNull
         public HeadToHeadTemplateData build() {
             return new HeadToHeadTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
                     mHeadToHeadTitle,
                     mHeadToHeadFirstCompetitorIcon,
                     mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText,
diff --git a/core/java/android/app/smartspace/uitemplatedata/Icon.java b/core/java/android/app/smartspace/uitemplatedata/Icon.java
index 2b1f420..6bdc926 100644
--- a/core/java/android/app/smartspace/uitemplatedata/Icon.java
+++ b/core/java/android/app/smartspace/uitemplatedata/Icon.java
@@ -69,7 +69,10 @@
         return mContentDescription;
     }
 
-    /** Return shouldTint value. The default value is true. */
+    /**
+     * Return shouldTint value, which means whether should tint the icon with the system's theme
+     * color. The default value is true.
+     */
     public boolean shouldTint() {
         return mShouldTint;
     }
@@ -155,7 +158,7 @@
         }
 
         /**
-         * Sets should tint icon.
+         * Sets should tint icon with the system's theme color.
          */
         @NonNull
         public Builder setShouldTint(boolean shouldTint) {
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
index 9c8330d..48af9c1 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java
@@ -62,18 +62,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull Icon subCardIcon,
             @Nullable Text subCardText,
             @Nullable TapAction subCardAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubCardIcon = subCardIcon;
         mSubCardText = subCardText;
         mSubCardAction = subCardAction;
@@ -196,9 +208,14 @@
         @NonNull
         public SubCardTemplateData build() {
             return new SubCardTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubCardIcon,
                     mSubCardText,
                     mSubCardAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
index 7df5238..38692cd 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java
@@ -63,18 +63,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @NonNull List<Text> subImageTexts,
             @NonNull List<Icon> subImages,
             @Nullable TapAction subImageAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubImageTexts = subImageTexts;
         mSubImages = subImages;
         mSubImageAction = subImageAction;
@@ -193,9 +205,14 @@
         @NonNull
         public SubImageTemplateData build() {
             return new SubImageTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubImageTexts,
                     mSubImages,
                     mSubImageAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
index 6f6034d..b1535f1 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java
@@ -62,18 +62,30 @@
             @Nullable Text titleText,
             @Nullable Icon titleIcon,
             @Nullable Text subtitleText,
-            @Nullable Icon subTitleIcon,
+            @Nullable Icon subtitleIcon,
             @Nullable TapAction primaryTapAction,
+            @Nullable SubItemLoggingInfo primaryLoggingInfo,
             @Nullable Text supplementalSubtitleText,
             @Nullable Icon supplementalSubtitleIcon,
             @Nullable TapAction supplementalSubtitleTapAction,
+            @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo,
+            @Nullable Text supplementalText,
+            @Nullable Icon supplementalIcon,
+            @Nullable TapAction supplementalTapAction,
+            @Nullable SubItemLoggingInfo supplementalLoggingInfo,
             @Nullable Text supplementalAlarmText,
+            int layoutWeight,
             @Nullable Icon subListIcon,
             @NonNull List<Text> subListTexts,
             @Nullable TapAction subListAction) {
-        super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
-                supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
-                supplementalAlarmText);
+        super(templateType, titleText, titleIcon, subtitleText, subtitleIcon,
+                primaryTapAction, primaryLoggingInfo,
+                supplementalSubtitleText, supplementalSubtitleIcon,
+                supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo,
+                supplementalText, supplementalIcon,
+                supplementalTapAction, supplementalLoggingInfo,
+                supplementalAlarmText, layoutWeight);
+
         mSubListIcon = subListIcon;
         mSubListTexts = subListTexts;
         mSubListAction = subListAction;
@@ -196,9 +208,14 @@
         @NonNull
         public SubListTemplateData build() {
             return new SubListTemplateData(getTemplateType(), getTitleText(),
-                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
+                    getTitleIcon(), getSubtitleText(), getSubtitleIcon(),
+                    getPrimaryTapAction(), getPrimaryLoggingInfo(),
                     getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
-                    getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon,
+                    getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(),
+                    getSupplementalText(), getSupplementalIcon(),
+                    getSupplementalTapAction(), getSupplementalLoggingInfo(),
+                    getSupplementalAlarmText(), getLayoutWeight(),
+                    mSubListIcon,
                     mSubListTexts,
                     mSubListAction);
         }
diff --git a/core/java/android/app/smartspace/uitemplatedata/TapAction.java b/core/java/android/app/smartspace/uitemplatedata/TapAction.java
index 83ff6ab..b8e1afb 100644
--- a/core/java/android/app/smartspace/uitemplatedata/TapAction.java
+++ b/core/java/android/app/smartspace/uitemplatedata/TapAction.java
@@ -58,7 +58,13 @@
     private final UserHandle mUserHandle;
 
     @Nullable
-    private Bundle mExtras;
+    private final Bundle mExtras;
+
+    /**
+     * Whether the tap action's result should be shown on the lockscreen (e.g. turn off the
+     * flashlight can be done on LS bypassing the keyguard). Default value is false.
+     */
+    private final boolean mShouldShowOnLockscreen;
 
     TapAction(@NonNull Parcel in) {
         mId = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
@@ -66,16 +72,18 @@
         mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
         mUserHandle = in.readTypedObject(UserHandle.CREATOR);
         mExtras = in.readBundle();
+        mShouldShowOnLockscreen = in.readBoolean();
     }
 
     private TapAction(@Nullable CharSequence id, @Nullable Intent intent,
             @Nullable PendingIntent pendingIntent, @Nullable UserHandle userHandle,
-            @Nullable Bundle extras) {
+            @Nullable Bundle extras, boolean shouldShowOnLockscreen) {
         mId = id;
         mIntent = intent;
         mPendingIntent = pendingIntent;
         mUserHandle = userHandle;
         mExtras = extras;
+        mShouldShowOnLockscreen = shouldShowOnLockscreen;
     }
 
     /** Returns the unique id of the tap action. */
@@ -110,6 +118,14 @@
         return mExtras;
     }
 
+    /**
+     * Whether the tap action's result should be shown on the lockscreen. If true, the tap action's
+     * handling should bypass the keyguard. Default value is false.
+     */
+    public boolean shouldShowOnLockscreen() {
+        return mShouldShowOnLockscreen;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
         TextUtils.writeToParcel(mId, out, flags);
@@ -117,6 +133,7 @@
         out.writeTypedObject(mPendingIntent, flags);
         out.writeTypedObject(mUserHandle, flags);
         out.writeBundle(mExtras);
+        out.writeBoolean(mShouldShowOnLockscreen);
     }
 
     @Override
@@ -158,6 +175,7 @@
                 + ", mPendingIntent=" + mPendingIntent
                 + ", mUserHandle=" + mUserHandle
                 + ", mExtras=" + mExtras
+                + ", mShouldShowOnLockscreen=" + mShouldShowOnLockscreen
                 + '}';
     }
 
@@ -174,14 +192,16 @@
         private PendingIntent mPendingIntent;
         private UserHandle mUserHandle;
         private Bundle mExtras;
+        private boolean mShouldShowOnLockScreen;
 
         /**
-         * A builder for {@link TapAction}.
+         * A builder for {@link TapAction}. By default sets should_show_on_lockscreen to false.
          *
          * @param id A unique Id of this {@link TapAction}.
          */
         public Builder(@NonNull CharSequence id) {
             mId = Objects.requireNonNull(id);
+            mShouldShowOnLockScreen = false;
         }
 
         /**
@@ -222,6 +242,16 @@
         }
 
         /**
+         * Sets whether the tap action's result should be shown on the lockscreen, to bypass the
+         * keyguard when the tap action is triggered.
+         */
+        @NonNull
+        public Builder setShouldShowOnLockscreen(@NonNull boolean shouldShowOnLockScreen) {
+            mShouldShowOnLockScreen = shouldShowOnLockScreen;
+            return this;
+        }
+
+        /**
          * Builds a new SmartspaceTapAction instance.
          *
          * @throws IllegalStateException if the tap action is empty.
@@ -231,7 +261,8 @@
             if (mIntent == null && mPendingIntent == null && mExtras == null) {
                 throw new IllegalStateException("Please assign at least 1 valid tap field");
             }
-            return new TapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras);
+            return new TapAction(mId, mIntent, mPendingIntent, mUserHandle, mExtras,
+                    mShouldShowOnLockScreen);
         }
     }
 }
diff --git a/core/java/android/app/smartspace/uitemplatedata/Text.java b/core/java/android/app/smartspace/uitemplatedata/Text.java
index b733394..e1afce7 100644
--- a/core/java/android/app/smartspace/uitemplatedata/Text.java
+++ b/core/java/android/app/smartspace/uitemplatedata/Text.java
@@ -131,16 +131,6 @@
         }
 
         /**
-         * A builder for {@link Text} with specifying {@link TextUtils.TruncateAt} type, and by
-         * default set the max lines to 1.
-         */
-        public Builder(@NonNull CharSequence text, @NonNull TextUtils.TruncateAt truncateAtType) {
-            mText = Objects.requireNonNull(text);
-            mTruncateAtType = Objects.requireNonNull(truncateAtType);
-            mMaxLines = 1;
-        }
-
-        /**
          * Sets truncateAtType, where the text content should be truncated if not all the content
          * can be presented.
          */
diff --git a/core/java/android/app/usage/BroadcastResponseStats.java b/core/java/android/app/usage/BroadcastResponseStats.java
index 5acc3dda..e1d37e1 100644
--- a/core/java/android/app/usage/BroadcastResponseStats.java
+++ b/core/java/android/app/usage/BroadcastResponseStats.java
@@ -23,6 +23,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Class containing a collection of stats related to response events started from an app
  * after receiving a broadcast.
@@ -32,17 +34,30 @@
 @SystemApi
 public final class BroadcastResponseStats implements Parcelable {
     private final String mPackageName;
+    private final long mId;
     private int mBroadcastsDispatchedCount;
     private int mNotificationsPostedCount;
     private int mNotificationsUpdatedCount;
     private int mNotificationsCancelledCount;
 
-    public BroadcastResponseStats(@NonNull String packageName) {
+    /**
+     * Creates a new {@link BroadcastResponseStats} object that contain the stats for broadcasts
+     * with {@code id} (specified using
+     * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)} by the sender) that
+     * were sent to {@code packageName}.
+     *
+     * @param packageName the name of the package that broadcasts were sent to.
+     * @param id the ID specified by the sender using
+     *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     */
+    public BroadcastResponseStats(@NonNull String packageName, @IntRange(from = 1) long id) {
         mPackageName = packageName;
+        mId = id;
     }
 
     private BroadcastResponseStats(@NonNull Parcel in) {
         mPackageName = in.readString8();
+        mId = in.readLong();
         mBroadcastsDispatchedCount = in.readInt();
         mNotificationsPostedCount = in.readInt();
         mNotificationsUpdatedCount = in.readInt();
@@ -58,6 +73,14 @@
     }
 
     /**
+     * @return the ID of the broadcasts that the stats in this object correspond to.
+     */
+    @IntRange(from = 1)
+    public long getId() {
+        return mId;
+    }
+
+    /**
      * Returns the total number of broadcasts that were dispatched to the app by the caller.
      *
      * <b> Note that the returned count will only include the broadcasts that the caller explicitly
@@ -148,9 +171,35 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || !(obj instanceof BroadcastResponseStats)) {
+            return false;
+        }
+        final BroadcastResponseStats other = (BroadcastResponseStats) obj;
+        return this.mBroadcastsDispatchedCount == other.mBroadcastsDispatchedCount
+                && this.mNotificationsPostedCount == other.mNotificationsPostedCount
+                && this.mNotificationsUpdatedCount == other.mNotificationsUpdatedCount
+                && this.mNotificationsCancelledCount == other.mNotificationsCancelledCount
+                && this.mId == other.mId
+                && this.mPackageName.equals(other.mPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName, mId, mBroadcastsDispatchedCount,
+                mNotificationsPostedCount, mNotificationsUpdatedCount,
+                mNotificationsCancelledCount);
+    }
+
+    @Override
     public @NonNull String toString() {
         return "stats {"
-                + "broadcastsSent=" + mBroadcastsDispatchedCount
+                + "package=" + mPackageName
+                + ",id=" + mId
+                + ",broadcastsSent=" + mBroadcastsDispatchedCount
                 + ",notificationsPosted=" + mNotificationsPostedCount
                 + ",notificationsUpdated=" + mNotificationsUpdatedCount
                 + ",notificationsCancelled=" + mNotificationsCancelledCount
@@ -165,6 +214,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
         dest.writeString8(mPackageName);
+        dest.writeLong(mId);
         dest.writeInt(mBroadcastsDispatchedCount);
         dest.writeInt(mNotificationsPostedCount);
         dest.writeInt(mNotificationsUpdatedCount);
diff --git a/apex/media/aidl/stable/android/media/Session2Token.aidl b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
similarity index 80%
rename from apex/media/aidl/stable/android/media/Session2Token.aidl
rename to core/java/android/app/usage/BroadcastResponseStatsList.aidl
index c5980e9..7380359 100644
--- a/apex/media/aidl/stable/android/media/Session2Token.aidl
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 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,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.app.usage;
 
-parcelable Session2Token;
+/** {@hide} */
+parcelable BroadcastResponseStatsList;
\ No newline at end of file
diff --git a/core/java/android/app/usage/BroadcastResponseStatsList.java b/core/java/android/app/usage/BroadcastResponseStatsList.java
new file mode 100644
index 0000000..4d2ff286
--- /dev/null
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.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.app.usage;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public final class BroadcastResponseStatsList implements Parcelable {
+    private List<BroadcastResponseStats> mBroadcastResponseStats;
+
+    public BroadcastResponseStatsList(
+            @NonNull List<BroadcastResponseStats> broadcastResponseStats) {
+        mBroadcastResponseStats = broadcastResponseStats;
+    }
+
+    private BroadcastResponseStatsList(@NonNull Parcel in) {
+        mBroadcastResponseStats = new ArrayList<>();
+        final byte[] bytes = in.readBlob();
+        final Parcel data = Parcel.obtain();
+        try {
+            data.unmarshall(bytes, 0, bytes.length);
+            data.setDataPosition(0);
+            data.readTypedList(mBroadcastResponseStats, BroadcastResponseStats.CREATOR);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @NonNull
+    public List<BroadcastResponseStats> getList() {
+        return mBroadcastResponseStats == null ? Collections.emptyList() : mBroadcastResponseStats;
+    }
+
+    @Override
+    public @ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
+        final Parcel data = Parcel.obtain();
+        try {
+            data.writeTypedList(mBroadcastResponseStats);
+            dest.writeBlob(data.marshall());
+        } finally {
+            data.recycle();
+        }
+    }
+
+    public static final @NonNull Creator<BroadcastResponseStatsList> CREATOR =
+            new Creator<BroadcastResponseStatsList>() {
+                @Override
+                public @NonNull BroadcastResponseStatsList createFromParcel(
+                        @NonNull Parcel source) {
+                    return new BroadcastResponseStatsList(source);
+                }
+
+                @Override
+                public @NonNull BroadcastResponseStatsList[] newArray(int size) {
+                    return new BroadcastResponseStatsList[size];
+                }
+            };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 6f8fea1..a430714 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
 import android.app.usage.UsageEvents;
 import android.content.pm.ParceledListSlice;
 
@@ -73,9 +74,11 @@
     void forceUsageSourceSettingRead();
     long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
-    BroadcastResponseStats queryBroadcastResponseStats(
+    BroadcastResponseStatsList queryBroadcastResponseStats(
             String packageName, long id, String callingPackage, int userId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
     void clearBroadcastResponseStats(String packageName, long id, String callingPackage,
             int userId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
+    void clearBroadcastEvents(String callingPackage, int userId);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index b81c62d..d7152b3 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1397,29 +1397,46 @@
      * Returns the broadcast response stats since the last boot corresponding to
      * {@code packageName} and {@code id}.
      *
-     * <p>Broadcast response stats will include the aggregated data of what actions an app took upon
-     * receiving a broadcast. This data will consider the broadcasts that the caller sent to
+     * <p> Broadcast response stats will include the aggregated data of what actions an app took
+     * upon receiving a broadcast. This data will consider the broadcasts that the caller sent to
      * {@code packageName} and explicitly requested to record the response events using
      * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
      *
-     * @param packageName The name of the package that the caller wants to query for.
-     * @param id The ID corresponding to the broadcasts that the caller wants to query for. This is
-     *           the ID the caller specifies when requesting a broadcast response event to be
-     *           recorded using {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     * <p> The returned list could one or more {@link BroadcastResponseStats} objects or be empty
+     * depending on the {@code packageName} and {@code id} and whether there is any data
+     * corresponding to these. If the {@code packageName} is not {@code null} and {@code id} is
+     * {@code > 0}, then the returned list would contain at most one {@link BroadcastResponseStats}
+     * object. Otherwise, the returned list could contain more than one
+     * {@link BroadcastResponseStats} object in no particular order.
      *
-     * @return the broadcast response stats corresponding to {@code packageName} and {@code id}.
+     * <p> Note: It is possible that same {@code id} was used for broadcasts sent to different
+     * packages. So, callers can query the data corresponding to
+     * all broadcasts with a particular {@code id} by passing {@code packageName} as {@code null}.
      *
+     * @param packageName The name of the package that the caller wants to query for
+     *                    or {@code null} to indicate that data corresponding to all packages
+     *                    should be returned.
+     * @param id The ID corresponding to the broadcasts that the caller wants to query for, or
+     *           {@code 0} to indicate that data corresponding to all IDs should be returned.
+     *           This is the ID the caller specifies when requesting a broadcast response event
+     *           to be recorded using
+     *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+     *
+     * @return the list of broadcast response stats corresponding to {@code packageName}
+     *         and {@code id}.
+     *
+     * @see #clearBroadcastResponseStats(String, long)
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @UserHandleAware
     @NonNull
-    public BroadcastResponseStats queryBroadcastResponseStats(
-            @NonNull String packageName, @IntRange(from = 1) long id) {
+    public List<BroadcastResponseStats> queryBroadcastResponseStats(
+            @Nullable String packageName, @IntRange(from = 0) long id) {
         try {
             return mService.queryBroadcastResponseStats(packageName, id,
-                    mContext.getOpPackageName(), mContext.getUserId());
+                    mContext.getOpPackageName(), mContext.getUserId()).getList();
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -1428,12 +1445,15 @@
     /**
      * Clears the broadcast response stats corresponding to {@code packageName} and {@code id}.
      *
-     * When a caller uses this API, stats related to the events occurring till that point will be
-     * cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
+     * <p> When a caller uses this API, stats related to the events occurring till that point will
+     * be cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
      * return stats related to events occurring after this.
      *
-     * @param packageName The name of the package that the caller wants to clear the data for.
-     * @param id The ID corresponding to the broadcasts that the caller wants to clear the data for.
+     * @param packageName The name of the package that the caller wants to clear the data for or
+     *                    {@code null} to indicate that data corresponding to all packages should
+     *                    be cleared.
+     * @param id The ID corresponding to the broadcasts that the caller wants to clear the data
+     *           for, or {code 0} to indicate that data corresponding to all IDs should be deleted.
      *           This is the ID the caller specifies when requesting a broadcast response event
      *           to be recorded using
      *           {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
@@ -1444,8 +1464,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @UserHandleAware
-    public void clearBroadcastResponseStats(@NonNull String packageName,
-            @IntRange(from = 1) long id) {
+    public void clearBroadcastResponseStats(@Nullable String packageName,
+            @IntRange(from = 0) long id) {
         try {
             mService.clearBroadcastResponseStats(packageName, id,
                     mContext.getOpPackageName(), mContext.getUserId());
@@ -1453,4 +1473,19 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Clears the broadcast events that were sent by the caller uid.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    @UserHandleAware
+    public void clearBroadcastEvents() {
+        try {
+            mService.clearBroadcastEvents(mContext.getOpPackageName(), mContext.getUserId());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 4e00da1..47bec61 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -49,21 +49,19 @@
     /**
      * Requests the continuous updates of proximity signal via the provided callback,
      * until the given callback is unregistered. Currently, AttentionManagerService only
-     * anticipates one client and updates one client at a time. If a new client wants to
-     * onboard to receiving Proximity updates, please make a feature request to make proximity
-     * feature multi-client before depending on this feature.
+     * anticipates one client and updates one client at a time.
      *
      * @param callback      a callback that receives the proximity updates
      * @return {@code true} if the registration should succeed.
      */
-    public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback);
+    public abstract boolean onStartProximityUpdates(ProximityUpdateCallbackInternal callback);
 
     /**
      * Requests to stop providing continuous updates until the callback is registered.
      *
      * @param callback a callback that was used in {@link #onStartProximityUpdates}
      */
-    public abstract void onStopProximityUpdates(ProximityCallbackInternal callback);
+    public abstract void onStopProximityUpdates(ProximityUpdateCallbackInternal callback);
 
     /** Internal interface for attention callback. */
     public abstract static class AttentionCallbackInternal {
@@ -85,7 +83,7 @@
     }
 
     /** Internal interface for proximity callback. */
-    public abstract static class ProximityCallbackInternal {
+    public abstract static class ProximityUpdateCallbackInternal {
         /**
          * @param distance the estimated distance of the user (in meter)
          * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d50a6ba..02d140f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -48,7 +48,6 @@
 import android.util.ArrayMap;
 import android.view.Surface;
 
-import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -148,6 +147,8 @@
                         }
                     }
                 };
+        @Nullable
+        private VirtualAudioDevice mVirtualAudioDevice;
 
         private VirtualDevice(
                 IVirtualDeviceManager service,
@@ -221,7 +222,8 @@
          * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}.
+         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
+         * not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
          * not create the virtual display.
@@ -235,7 +237,7 @@
                 @IntRange(from = 1) int densityDpi,
                 @Nullable Surface surface,
                 @VirtualDisplayFlag int flags,
-                @NonNull @CallbackExecutor Executor executor,
+                @Nullable @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
             // TODO(b/205343547): Handle display groups properly instead of creating a new display
             //  group for every new virtual display created using this API.
@@ -251,12 +253,12 @@
                             .setFlags(getVirtualDisplayFlags(flags))
                             .build(),
                     callback,
-                    Objects.requireNonNull(executor));
+                    executor);
         }
 
         /**
-         * Closes the virtual device, stopping and tearing down any virtual displays,
-         * audio policies, and event injection that's currently in progress.
+         * Closes the virtual device, stopping and tearing down any virtual displays, associated
+         * virtual audio device, and event injection that's currently in progress.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void close() {
@@ -265,6 +267,10 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
+            if (mVirtualAudioDevice != null) {
+                mVirtualAudioDevice.close();
+                mVirtualAudioDevice = null;
+            }
         }
 
         /**
@@ -351,8 +357,10 @@
          * Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
          * or injecting audio from another device.
          *
-         * <p>Note: This object does not support capturing privileged playback, such as voice call
-         * audio.
+         * <p>Note: One {@link VirtualDevice} can only create one {@link VirtualAudioDevice}, so
+         * calling this method multiple times will return the same instance. When
+         * {@link VirtualDevice#close()} is called, the associated {@link VirtualAudioDevice} will
+         * also be closed automatically.
          *
          * @param display The target virtual display to capture from and inject into.
          * @param executor The {@link Executor} object for the thread on which to execute
@@ -368,7 +376,11 @@
                 @NonNull VirtualDisplay display,
                 @Nullable Executor executor,
                 @Nullable AudioConfigurationChangeCallback callback) {
-            return new VirtualAudioDevice(mContext, mVirtualDevice, display, executor, callback);
+            if (mVirtualAudioDevice == null) {
+                mVirtualAudioDevice = new VirtualAudioDevice(
+                        mContext, mVirtualDevice, display, executor, callback);
+            }
+            return mVirtualAudioDevice;
         }
 
         /**
diff --git a/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
index 5c246d3..c816da7 100644
--- a/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
+++ b/core/java/android/companion/virtual/audio/UserRestrictionsDetector.java
@@ -60,7 +60,6 @@
     /** Registers user restrictions change. */
     void register(@NonNull UserRestrictionsCallback callback) {
         mUserRestrictionsCallback = callback;
-
         IntentFilter filter = new IntentFilter();
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
         mContext.registerReceiver(/* receiver= */ this, filter);
@@ -73,8 +72,10 @@
 
     /** Unregisters user restrictions change. */
     void unregister() {
-        mUserRestrictionsCallback = null;
-        mContext.unregisterReceiver(/* receiver= */ this);
+        if (mUserRestrictionsCallback != null) {
+            mUserRestrictionsCallback = null;
+            mContext.unregisterReceiver(/* receiver= */ this);
+        }
     }
 
     @Override
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
index 38e37ec..3f7299f 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.hardware.display.VirtualDisplay;
 import android.media.AudioFormat;
+import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
 import android.os.RemoteException;
@@ -96,7 +97,7 @@
 
         if (mOngoingSession != null && mOngoingSession.getAudioInjection() != null) {
             throw new IllegalStateException("Cannot start an audio injection while a session is "
-                    + "ongoing. Call close() on this device first to end the previous injection.");
+                    + "ongoing. Call close() on this device first to end the previous session.");
         }
         if (mOngoingSession == null) {
             mOngoingSession = new VirtualAudioSession(mContext, mCallback, mExecutor);
@@ -114,6 +115,9 @@
     /**
      * Begins recording audio emanating from this device.
      *
+     * <p>Note: This method does not support capturing privileged playback, which means the
+     * application can opt out of capturing by {@link AudioManager#setAllowedCapturePolicy(int)}.
+     *
      * @return An {@link AudioCapture} containing the recorded audio.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
@@ -150,6 +154,7 @@
         return mOngoingSession != null ? mOngoingSession.getAudioInjection() : null;
     }
 
+    /** Stops audio capture and injection then releases all the resources */
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     @Override
     public void close() {
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioSession.java b/core/java/android/companion/virtual/audio/VirtualAudioSession.java
index bc71bd6..c6a1045 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioSession.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioSession.java
@@ -120,7 +120,7 @@
     @NonNull
     public AudioInjection startAudioInjection(@NonNull AudioFormat injectionFormat) {
         Objects.requireNonNull(injectionFormat, "injectionFormat must not be null");
-        mUserRestrictionsDetector.register(/* callback= */ this);
+
         synchronized (mLock) {
             if (mAudioInjection != null) {
                 throw new IllegalStateException(
@@ -130,6 +130,8 @@
             mInjectionFormat = injectionFormat;
             mAudioInjection = new AudioInjection();
             mAudioInjection.play();
+
+            mUserRestrictionsDetector.register(/* callback= */ this);
             mAudioInjection.setSilent(mUserRestrictionsDetector.isUnmuteMicrophoneDisallowed());
             return mAudioInjection;
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0fed733..8f82a0a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3525,7 +3525,7 @@
     public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
 
     /**
-     * Connect to an application service, creating it if needed.  This defines
+     * Connects to an application service, creating it if needed.  This defines
      * a dependency between your application and the service.  The given
      * <var>conn</var> will receive the service object when it is created and be
      * told if it dies and restarts.  The service will be considered required
@@ -3540,11 +3540,8 @@
      * will be invoked instead of
      * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}.
      *
-     * <p>This method will throw {@link SecurityException} if the calling app does not
-     * have permission to bind to the given service.
-     *
-     * <p class="note">Note: this method <em>cannot be called from a
-     * {@link BroadcastReceiver} component</em>.  A pattern you can use to
+     * <p class="note"><b>Note:</b> This method <em>cannot</em> be called from a
+     * {@link BroadcastReceiver} component.  A pattern you can use to
      * communicate from a BroadcastReceiver to a Service is to call
      * {@link #startService} with the arguments containing the command to be
      * sent, with the service calling its
@@ -3559,33 +3556,34 @@
      *      specify an explicit component name.
      * @param conn Receives information as the service is started and stopped.
      *      This must be a valid ServiceConnection object; it must not be null.
-     * @param flags Operation options for the binding.  May be 0,
-     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
-     *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
-     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
-     *          {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY},
-     *          {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}.
-     * @return {@code true} if the system is in the process of bringing up a
+     * @param flags Operation options for the binding. Can be:
+     *      <ul>
+     *          <li>0
+     *          <li>{@link #BIND_AUTO_CREATE}
+     *          <li>{@link #BIND_DEBUG_UNBIND}
+     *          <li>{@link #BIND_NOT_FOREGROUND}
+     *          <li>{@link #BIND_ABOVE_CLIENT}
+     *          <li>{@link #BIND_ALLOW_OOM_MANAGEMENT}
+     *          <li>{@link #BIND_WAIVE_PRIORITY}
+     *          <li>{@link #BIND_IMPORTANT}
+     *          <li>{@link #BIND_ADJUST_WITH_ACTIVITY}
+     *          <li>{@link #BIND_NOT_PERCEPTIBLE}
+     *          <li>{@link #BIND_INCLUDE_CAPABILITIES}
+     *      </ul>
+     *
+      * @return {@code true} if the system is in the process of bringing up a
      *         service that your client has permission to bind to; {@code false}
      *         if the system couldn't find the service or if your client doesn't
      *         have permission to bind to it. You should call {@link #unbindService}
      *         to release the connection even if this method returned {@code false}.
      *
-     * @throws SecurityException If the caller does not have permission to access the service
-     * or the service can not be found.
+     * @throws SecurityException If the caller does not have permission to
+     *      access the service or the service cannot be found. Call
+     *      {@link #unbindService} to release the connection when this exception
+     *      is thrown.
      *
      * @see #unbindService
      * @see #startService
-     * @see #BIND_AUTO_CREATE
-     * @see #BIND_DEBUG_UNBIND
-     * @see #BIND_NOT_FOREGROUND
-     * @see #BIND_ABOVE_CLIENT
-     * @see #BIND_ALLOW_OOM_MANAGEMENT
-     * @see #BIND_WAIVE_PRIORITY
-     * @see #BIND_IMPORTANT
-     * @see #BIND_ADJUST_WITH_ACTIVITY
-     * @see #BIND_NOT_PERCEPTIBLE
-     * @see #BIND_INCLUDE_CAPABILITIES
      */
     public abstract boolean bindService(@RequiresPermission Intent service,
             @NonNull ServiceConnection conn, @BindServiceFlags int flags);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e5cffb1..0aa25ef 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -9209,6 +9209,16 @@
     }
 
     /**
+     * Returns the total size of the extras in bytes, or 0 if no extras are present.
+     * @hide
+     */
+    public int getExtrasTotalSize() {
+        return (mExtras != null)
+                ? mExtras.getSize()
+                : 0;
+    }
+
+    /**
      * @return Whether {@link #maybeStripForHistory} will return an lightened intent or
      * return itself as-is.
      * @hide
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index 21398f6..660a7f0 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -63,8 +63,12 @@
      * happen, for example, if the application hosting the service it is bound to
      * has been updated.
      *
-     * @param name The concrete component name of the service whose
-     * connection is dead.
+     * <p class="note"><b>Note:</b> The app that requested the binding must call
+     * {@link Context#unbindService(ServiceConnection)} to release the tracking
+     * resources associated with this ServiceConnection even if this callback was
+     * invoked following {@link Context#bindService Context.bindService() bindService()}.
+     *
+     * @param name The concrete component name of the service whose connection is dead.
      */
     default void onBindingDied(ComponentName name) {
     }
@@ -72,10 +76,10 @@
     /**
      * Called when the service being bound has returned {@code null} from its
      * {@link android.app.Service#onBind(Intent) onBind()} method.  This indicates
-     * that the attempting service binding represented by this ServiceConnection
+     * that the attempted service binding represented by this ServiceConnection
      * will never become usable.
      *
-     * <p class="note">The app which requested the binding must still call
+     * <p class="note"><b>Note:</b> The app that requested the binding must still call
      * {@link Context#unbindService(ServiceConnection)} to release the tracking
      * resources associated with this ServiceConnection even if this callback was
      * invoked following {@link Context#bindService Context.bindService() bindService()}.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2528e16..fbb03ca 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,6 +32,7 @@
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.ArrayMap;
@@ -39,6 +40,7 @@
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
+import android.view.OnBackInvokedCallback;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Parcelling;
@@ -801,11 +803,24 @@
      */
     public static final int PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE = 1 << 2;
 
+
+    /**
+     * If false, {@link android.view.KeyEvent#KEYCODE_BACK} related events will be forwarded to
+     * the Activities, Dialogs and Views and {@link android.app.Activity#onBackPressed()},
+     * {@link android.app.Dialog#onBackPressed} will be called. Otherwise, those events will be
+     * replaced by a call to {@link OnBackInvokedCallback#onBackInvoked()} on the focused window.
+     *
+     * @hide
+     * @see android.R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback
+     */
+    public static final int PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK = 1 << 3;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
             PRIVATE_FLAG_EXT_PROFILEABLE,
             PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION,
             PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE,
+            PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationInfoPrivateFlagsExt {}
@@ -1683,6 +1698,7 @@
                 pw.println(prefix + "localeConfigRes=0x"
                         + Integer.toHexString(localeConfigRes));
             }
+            pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled());
         }
         pw.println(prefix + "createTimestamp=" + createTimestamp);
         if (mKnownActivityEmbeddingCerts != null) {
@@ -1850,7 +1866,7 @@
     }
 
     public ApplicationInfo() {
-        createTimestamp = System.currentTimeMillis();
+        createTimestamp = SystemClock.uptimeMillis();
     }
 
     public ApplicationInfo(ApplicationInfo orig) {
@@ -1924,7 +1940,7 @@
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
         localeConfigRes = orig.localeConfigRes;
-        createTimestamp = System.currentTimeMillis();
+        createTimestamp = SystemClock.uptimeMillis();
     }
 
     public String toString() {
@@ -2566,6 +2582,17 @@
     }
 
     /**
+     * Returns whether the application will use the {@link android.view.OnBackInvokedCallback}
+     * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related
+     * callbacks.
+     *
+     * @hide
+     */
+    public boolean isOnBackInvokedCallbackEnabled() {
+        return ((privateFlagsExt & PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK)) != 0;
+    }
+
+    /**
      * @hide
      */
     @Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 67a2dc8..76c998b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2421,15 +2421,6 @@
         /** {@hide} */
         private static final int[] NO_SESSIONS = {};
 
-        /** @hide */
-        @IntDef(prefix = { "SESSION_" }, value = {
-                SESSION_NO_ERROR,
-                SESSION_VERIFICATION_FAILED,
-                SESSION_ACTIVATION_FAILED,
-                SESSION_UNKNOWN_ERROR,
-                SESSION_CONFLICT})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface SessionErrorCode {}
         /**
          * @deprecated use {@link #SESSION_NO_ERROR}.
          */
@@ -3113,7 +3104,7 @@
          * If something went wrong with a staged session, clients can check this error code to
          * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
          */
-        public @SessionErrorCode int getStagedSessionErrorCode() {
+        public int getStagedSessionErrorCode() {
             checkSessionIsStaged();
             return mSessionErrorCode;
         }
@@ -3128,7 +3119,7 @@
         }
 
         /** {@hide} */
-        public void setSessionErrorCode(@SessionErrorCode int errorCode, String errorMessage) {
+        public void setSessionErrorCode(int errorCode, String errorMessage) {
             mSessionErrorCode = errorCode;
             mSessionErrorMessage = errorMessage;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dc79ee6..31cb663 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2202,6 +2202,14 @@
      */
     public static final int INSTALL_FAILED_BAD_PERMISSION_GROUP = -127;
 
+    /**
+     * Installation failed return code: an error occurred during the activation phase of this
+     * session.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ACTIVATION_FAILED = -128;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -8035,6 +8043,7 @@
             return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
                     FrameworkPackageUserState.DEFAULT);
         } catch (PackageParser.PackageParserException e) {
+            Log.w(TAG, "Failure to parse package archive", e);
             return null;
         }
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 98cc8f6..e914432 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -59,6 +59,7 @@
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.content.pm.pkg.FrameworkPackageUserState;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
@@ -2417,9 +2418,28 @@
             Slog.i(TAG, newPermsMsg.toString());
         }
 
-        final List<PermissionManager.SplitPermissionInfo> splitPermissions =
-                ActivityThread.currentApplication().getSystemService(PermissionManager.class)
-                        .getSplitPermissions();
+        // Must build permission info manually for legacy code, which can be called before
+        // Appication is available through the app process, so the normal API doesn't work.
+        List<SplitPermissionInfoParcelable> splitPermissionParcelables;
+        try {
+            splitPermissionParcelables = ActivityThread.getPermissionManager()
+                    .getSplitPermissions();
+        } catch (RemoteException e) {
+            splitPermissionParcelables = Collections.emptyList();
+        }
+
+        int splitPermissionsSize = splitPermissionParcelables.size();
+        List<PermissionManager.SplitPermissionInfo> splitPermissions =
+                new ArrayList<>(splitPermissionsSize);
+        for (int index = 0; index < splitPermissionsSize; index++) {
+            SplitPermissionInfoParcelable splitPermissionParcelable =
+                    splitPermissionParcelables.get(index);
+            splitPermissions.add(new PermissionManager.SplitPermissionInfo(
+                    splitPermissionParcelable.getSplitPermission(),
+                    splitPermissionParcelable.getNewPermissions(),
+                    splitPermissionParcelable.getTargetSdk()
+            ));
+        }
 
         final int listSize = splitPermissions.size();
         for (int is = 0; is < listSize; is++) {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 41dd5bb3..dc7cd71 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2257,10 +2257,20 @@
     }
 
     /**
-     * Return true if the shortcut is included in specified surface.
+     * Return true if the shortcut is excluded from specified surface.
      */
-    public boolean isIncludedIn(@Surface int surface) {
-        return (mExcludedSurfaces & surface) == 0;
+    public boolean isExcludedFromSurfaces(@Surface int surface) {
+        return (mExcludedSurfaces & surface) != 0;
+    }
+
+    /**
+     * Returns a bitmask of all surfaces this shortcut is excluded from.
+     *
+     * @see ShortcutInfo.Builder#setExcludedFromSurfaces(int)
+     */
+    @Surface
+    public int getExcludedFromSurfaces() {
+        return mExcludedSurfaces;
     }
 
     /**
@@ -2522,7 +2532,7 @@
         if (isLongLived()) {
             sb.append("Liv");
         }
-        if (!isIncludedIn(SURFACE_LAUNCHER)) {
+        if (isExcludedFromSurfaces(SURFACE_LAUNCHER)) {
             sb.append("Hid-L");
         }
         sb.append("]");
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index ecd240d..24ae31e 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -199,6 +199,7 @@
                         certs.add(certsList);
                     }
                 }
+                typedArray.recycle();
             }
             return new ProviderResourceEntry(
                     authority,
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index ac1bcf3..1e4c9501 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -63,6 +63,7 @@
             D_FP32,
             DS_FP32UI8,
             S_UI8,
+            YCBCR_P010,
     })
     public @interface Format {
     }
@@ -96,6 +97,14 @@
     public static final int DS_FP32UI8   = 0x34;
     /** Format: 8 bits stencil */
     public static final int S_UI8        = 0x35;
+    /**
+     * <p>Android YUV P010 format.</p>
+     *
+     * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+     * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit
+     * little-endian value, with the lower 6 bits set to zero.
+     */
+    public static final int YCBCR_P010 = 0x36;
 
     // Note: do not rename, this field is used by native code
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -422,6 +431,7 @@
             case D_FP32:
             case DS_FP32UI8:
             case S_UI8:
+            case YCBCR_P010:
                 return true;
         }
         return false;
diff --git a/core/java/android/hardware/ISensorPrivacyListener.aidl b/core/java/android/hardware/ISensorPrivacyListener.aidl
index 5d40265..2ac21d2 100644
--- a/core/java/android/hardware/ISensorPrivacyListener.aidl
+++ b/core/java/android/hardware/ISensorPrivacyListener.aidl
@@ -24,6 +24,6 @@
     // the ones in
     //   frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
     // =============== Beginning of transactions used on native side as well ======================
-    void onSensorPrivacyChanged(boolean enabled);
+    void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled);
     // =============== End of transactions used on native side as well ============================
 }
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index 5571165..a392afd 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -24,34 +24,31 @@
     // the ones in
     //   frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
     // =============== Beginning of transactions used on native side as well ======================
-    boolean supportsSensorToggle(int sensor);
+    boolean supportsSensorToggle(int toggleType, int sensor);
 
     void addSensorPrivacyListener(in ISensorPrivacyListener listener);
 
-    void addIndividualSensorPrivacyListener(int userId, int sensor,
-            in ISensorPrivacyListener listener);
+    void addToggleSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
 
-    void removeIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
+    void removeToggleSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     boolean isSensorPrivacyEnabled();
 
-    boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+    boolean isCombinedToggleSensorPrivacyEnabled(int sensor);
+
+    boolean isToggleSensorPrivacyEnabled(int toggleType, int sensor);
 
     void setSensorPrivacy(boolean enable);
 
-    void setIndividualSensorPrivacy(int userId, int source, int sensor, boolean enable);
+    void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable);
 
-    void setIndividualSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
+    void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
     // =============== End of transactions used on native side as well ============================
 
-    void suppressIndividualSensorPrivacyReminders(int userId, int sensor, IBinder token,
+    void suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token,
             boolean suppress);
 
-    void addUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
-
-    void removeUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
-
     void showSensorUseDialog(int sensor);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 45d4c09..6113932 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,6 +17,7 @@
 package android.hardware;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -664,16 +665,20 @@
      * The first three elements provide the transform from the (arbitrary, possibly slowly drifting)
      * reference frame to the head frame. The magnitude of this vector is in range [0, &pi;]
      * radians, while the value of individual axes is in range [-&pi;, &pi;]. The next three
-     * elements provide the estimated rotational velocity of the user's head relative to itself, in
-     * radians per second.
+     * elements optionally provide the estimated rotational velocity of the user's head relative to
+     * itself, in radians per second. If a given sensor does not support determining velocity, these
+     * elements are set to 0.
      *
      * <ul>
      *  <li> values[0] : X component of Euler vector representing rotation</li>
      *  <li> values[1] : Y component of Euler vector representing rotation</li>
      *  <li> values[2] : Z component of Euler vector representing rotation</li>
-     *  <li> values[3] : X component of Euler vector representing angular velocity</li>
-     *  <li> values[4] : Y component of Euler vector representing angular velocity</li>
-     *  <li> values[5] : Z component of Euler vector representing angular velocity</li>
+     *  <li> values[3] : X component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
+     *  <li> values[4] : Y component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
+     *  <li> values[5] : Z component of Euler vector representing angular velocity (if
+     *  supported, otherwise 0)</li>
      * </ul>
      *
      * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES
@@ -820,6 +825,21 @@
      */
     public long timestamp;
 
+    /**
+     * Set to true when this is the first sensor event after a discontinuity.
+     *
+     * The exact meaning of discontinuity depends on the sensor type. For
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that
+     * the reference frame has suddenly and significantly changed, for example if the head tracking
+     * device was removed then put back.
+     *
+     * Note that this concept is either not relevant to or not supported by most sensor types,
+     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
+     * exception.
+     */
+    @SuppressLint("MutableBareField")
+    public boolean firstEventAfterDiscontinuity;
+
     @UnsupportedAppUsage
     SensorEvent(int valueSize) {
         values = new float[valueSize];
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
index 7b0092d..bac212a 100644
--- a/core/java/android/hardware/SensorEventCallback.java
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -16,8 +16,6 @@
 
 package android.hardware;
 
-import android.annotation.NonNull;
-
 /**
  * Used for receiving sensor additional information frames.
  */
@@ -54,21 +52,4 @@
      * reported from sensor hardware.
      */
     public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
-
-    /**
-     * Called when the next {@link android.hardware.SensorEvent SensorEvent} to be delivered via the
-     * {@link #onSensorChanged(SensorEvent) onSensorChanged} method represents the first event after
-     * a discontinuity.
-     *
-     * The exact meaning of discontinuity depends on the sensor type. For {@link
-     * android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that the
-     * reference frame has suddenly and significantly changed.
-     *
-     * Note that this concept is either not relevant to or not supported by most sensor types,
-     * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
-     * exception.
-     *
-     * @param sensor The {@link android.hardware.Sensor Sensor} which experienced the discontinuity.
-     */
-    public void onSensorDiscontinuity(@NonNull Sensor sensor) {}
 }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 47659a9..dec424c 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -639,7 +639,7 @@
     /**
      * Unregisters a listener for the sensors with which it is registered.
      *
-     * <p class="note"></p>
+     * <p class="note">
      * Note: Don't use this method with a one shot trigger sensor such as
      * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
      * Use {@link #cancelTriggerSensor(TriggerEventListener, Sensor)} instead.
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 79153d7..a3cc01c 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -36,7 +36,6 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -73,8 +72,6 @@
     public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName()
             + ".extra.all_sensors";
 
-    private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
-
     /**
      * Sensor constants which are used in {@link SensorPrivacyManager}
      */
@@ -161,36 +158,30 @@
 
     }
 
+
+    /**
+     * Constant for software toggle.
+     */
+    public static final int TOGGLE_TYPE_SOFTWARE =
+            SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+    /**
+     * Constant for hardware toggle.
+     */
+    public static final int TOGGLE_TYPE_HARDWARE =
+            SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
     /**
      * 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 {}
-
-    }
+    @IntDef(value = {
+            TOGGLE_TYPE_SOFTWARE,
+            TOGGLE_TYPE_HARDWARE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ToggleType {}
 
     /**
      * Types of state which can exist for the sensor privacy toggle
@@ -235,14 +226,57 @@
         /**
          * Callback invoked when the sensor privacy state changes.
          *
+         * @param params Parameters describing the new state
+         */
+        default void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params) {
+            onSensorPrivacyChanged(params.mSensor, params.mEnabled);
+        }
+
+        /**
+         * Callback invoked when the sensor privacy state changes.
+         *
          * @param sensor the sensor whose state is changing
          * @param enabled true if sensor privacy is enabled, false otherwise.
+         *
+         * @deprecated Please use
+         * {@link #onSensorPrivacyChanged(SensorPrivacyChangedParams)}
          */
+        @Deprecated
         void onSensorPrivacyChanged(int sensor, boolean enabled);
+
+        /**
+         * A class containing information about what the sensor privacy state has changed to.
+         */
+        class SensorPrivacyChangedParams {
+
+            private int mToggleType;
+            private int mSensor;
+            private boolean mEnabled;
+
+            private SensorPrivacyChangedParams(int toggleType, int sensor, boolean enabled) {
+                mToggleType = toggleType;
+                mSensor = sensor;
+                mEnabled = enabled;
+            }
+
+            public @ToggleType int getToggleType() {
+                return mToggleType;
+            }
+
+            public @Sensors.Sensor int getSensor() {
+                return mSensor;
+            }
+
+            public boolean isEnabled() {
+                return mEnabled;
+            }
+        }
     }
 
     private static final Object sInstanceLock = new Object();
 
+    private final Object mLock = new Object();
+
     @GuardedBy("sInstanceLock")
     private static SensorPrivacyManager sInstance;
 
@@ -252,12 +286,46 @@
     @NonNull
     private final ISensorPrivacyManager mService;
 
+    @GuardedBy("mLock")
+    private final ArrayMap<Pair<Integer, Integer>, Boolean> mToggleSupportCache = new ArrayMap<>();
+
     @NonNull
     private final ArrayMap<OnAllSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
 
+    /** Registered listeners */
+    @GuardedBy("mLock")
     @NonNull
-    private final ArrayMap<Pair<OnSensorPrivacyChangedListener, Integer>, ISensorPrivacyListener>
-            mIndividualListeners;
+    private final ArrayMap<OnSensorPrivacyChangedListener, Executor> mToggleListeners =
+            new ArrayMap<>();
+
+    /** Listeners registered using the deprecated APIs and which
+     * OnSensorPrivacyChangedListener they're using. */
+    @GuardedBy("mLock")
+    @NonNull
+    private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
+            OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
+
+    /** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
+     * listeners */
+    @NonNull
+    private final ISensorPrivacyListener mIToggleListener = new ISensorPrivacyListener.Stub() {
+        @Override
+        public void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled) {
+            synchronized (mLock) {
+                for (int i = 0; i < mToggleListeners.size(); i++) {
+                    OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
+                    mToggleListeners.valueAt(i).execute(() -> listener
+                            .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
+                                    .SensorPrivacyChangedParams(toggleType, sensor, enabled)));
+                }
+            }
+        }
+    };
+
+    /** Whether the singleton ISensorPrivacyListener has been registered */
+    @GuardedBy("mLock")
+    @NonNull
+    private boolean mToggleListenerRegistered = false;
 
     /**
      * Private constructor to ensure only a single instance is created.
@@ -266,7 +334,6 @@
         mContext = context;
         mService = service;
         mListeners = new ArrayMap<>();
-        mIndividualListeners = new ArrayMap<>();
     }
 
     /**
@@ -290,30 +357,56 @@
     }
 
     /**
+     * Returns the single instance of the SensorPrivacyManager.
+     *
+     * @hide
+     */
+    public static SensorPrivacyManager getInstance(Context context, ISensorPrivacyManager service) {
+        synchronized (sInstanceLock) {
+            sInstance = new SensorPrivacyManager(context, service);
+            return sInstance;
+        }
+    }
+
+    /**
      * Checks if the given toggle is supported on this device
      * @param sensor The sensor to check
      * @return whether the toggle for the sensor is supported on this device.
      */
     public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
+        return supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor);
+    }
+
+    /**
+     * Checks if the given toggle is supported on this device
+     * @param sensor The sensor to check
+     * @return whether the toggle for the sensor is supported on this device.
+     *
+     */
+    public boolean supportsSensorToggle(@ToggleType int toggleType, @Sensors.Sensor int sensor) {
         try {
-            Boolean val = mToggleSupportCache.get(sensor);
-            if (val == null) {
-                val = mService.supportsSensorToggle(sensor);
-                mToggleSupportCache.put(sensor, val);
+            Pair key = new Pair(toggleType, sensor);
+            synchronized (mLock) {
+                Boolean val = mToggleSupportCache.get(key);
+                if (val == null) {
+                    val = mService.supportsSensorToggle(toggleType, sensor);
+                    mToggleSupportCache.put(key, val);
+                }
+                return val;
             }
-            return val;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     *
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
      * @param sensor the sensor to listen to changes to
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
-     *                 privacy changes.
+     *                       privacy changes.
      *
      * @hide
      */
@@ -325,6 +418,7 @@
     }
 
     /**
+     *
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
@@ -336,19 +430,20 @@
      * @hide
      */
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
+    public void addSensorPrivacyListener(@Sensors.Sensor int sensor, int userId,
             @NonNull OnSensorPrivacyChangedListener listener) {
-        addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener);
+        addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
     }
 
     /**
+     *
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
      * @param sensor the sensor to listen to changes to
      * @param executor the executor to dispatch the callback on
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
-     *                 privacy changes.
+     *                       privacy changes.
      *
      * @hide
      */
@@ -356,61 +451,76 @@
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
             @NonNull OnSensorPrivacyChangedListener listener) {
-        Pair<OnSensorPrivacyChangedListener, Integer> key = new Pair<>(listener, sensor);
-        synchronized (mIndividualListeners) {
-            ISensorPrivacyListener iListener = mIndividualListeners.get(key);
-            if (iListener == null) {
-                iListener = new ISensorPrivacyListener.Stub() {
-                    @Override
-                    public void onSensorPrivacyChanged(boolean enabled) {
-                        executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
-                    }
-                };
-                mIndividualListeners.put(key, iListener);
+        Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
+        OnSensorPrivacyChangedListener toggleListener = new OnSensorPrivacyChangedListener() {
+            @Override
+            public void onSensorPrivacyChanged(SensorPrivacyChangedParams params) {
+                if (params.getSensor() == sensor) {
+                    listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+                }
             }
+            @Override
+            public void onSensorPrivacyChanged(int sensor, boolean enabled) {
+            }
+        };
 
-            try {
-                mService.addUserGlobalIndividualSensorPrivacyListener(sensor, iListener);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        synchronized (mLock) {
+            mLegacyToggleListeners.put(pair, toggleListener);
+            addSensorPrivacyListenerLocked(executor, toggleListener);
         }
     }
 
     /**
+     *
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
-     * @param sensor the sensor to listen to changes to
-     * @param executor the executor to dispatch the callback on
-     * @param userId the user's id
-     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
-     *                 privacy changes.
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
+     *                 sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
-            @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) {
-        synchronized (mIndividualListeners) {
-            ISensorPrivacyListener iListener = mIndividualListeners.get(listener);
-            if (iListener == null) {
-                iListener = new ISensorPrivacyListener.Stub() {
-                    @Override
-                    public void onSensorPrivacyChanged(boolean enabled) {
-                        executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
-                    }
-                };
-                mIndividualListeners.put(new Pair<>(listener, sensor), iListener);
-            }
+    public void addSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
+        addSensorPrivacyListener(mContext.getMainExecutor(), listener);
+    }
 
+    /**
+     *
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     *
+     * @param executor the executor to dispatch the callback on
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
+     *                 sensor privacy changes.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void addSensorPrivacyListener(@NonNull Executor executor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        synchronized (mLock) {
+            addSensorPrivacyListenerLocked(executor, listener);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void addSensorPrivacyListenerLocked(@NonNull Executor executor,
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        if (!mToggleListenerRegistered) {
             try {
-                mService.addIndividualSensorPrivacyListener(userId, sensor,
-                        iListener);
+                mService.addToggleSensorPrivacyListener(mIToggleListener);
+                mToggleListenerRegistered = true;
             } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+                e.rethrowFromSystemServer();
             }
         }
+        if (mToggleListeners.containsKey(listener)) {
+            throw new IllegalArgumentException("listener is already registered");
+        }
+        mToggleListeners.put(listener, executor);
     }
 
     /**
@@ -426,23 +536,66 @@
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void removeSensorPrivacyListener(@Sensors.Sensor int sensor,
             @NonNull OnSensorPrivacyChangedListener listener) {
-        synchronized (mListeners) {
-            for (int i = 0; i < mIndividualListeners.size(); i++) {
-                Pair<OnSensorPrivacyChangedListener, Integer> pair = mIndividualListeners.keyAt(i);
-                if (pair.second == sensor && pair.first.equals(listener)) {
-                    try {
-                        mService.removeIndividualSensorPrivacyListener(sensor,
-                                mIndividualListeners.valueAt(i));
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                    mIndividualListeners.removeAt(i--);
-                }
+        Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
+        synchronized (mLock) {
+            OnSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
+                    mLegacyToggleListeners.remove(pair);
+            if (onToggleSensorPrivacyChangedListener != null) {
+                removeSensorPrivacyListenerLocked(onToggleSensorPrivacyChangedListener);
             }
         }
     }
 
     /**
+     * Unregisters the specified listener from receiving notifications when the state of any sensor
+     * privacy changes.
+     *
+     * @param listener the {@link OnSensorPrivacyChangedListener} to be unregistered from
+     *                 notifications when sensor privacy changes.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public void removeSensorPrivacyListener(
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        synchronized (mLock) {
+            removeSensorPrivacyListenerLocked(listener);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void removeSensorPrivacyListenerLocked(
+            @NonNull OnSensorPrivacyChangedListener listener) {
+        mToggleListeners.remove(listener);
+        if (mToggleListeners.size() == 0) {
+            try {
+                mService.removeToggleSensorPrivacyListener(mIToggleListener);
+                mToggleListenerRegistered = false;
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns whether sensor privacy is currently enabled by software control for a specific
+     * sensor.
+     *
+     * @return true if sensor privacy is currently enabled, false otherwise.
+     *
+     * @deprecated Prefer to use {@link #isSensorPrivacyEnabled(int, int)}
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+    public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
+        return isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor);
+    }
+
+    /**
      * Returns whether sensor privacy is currently enabled for a specific sensor.
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
@@ -451,21 +604,29 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
-        return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT);
+    public boolean isSensorPrivacyEnabled(@ToggleType int toggleType,
+            @Sensors.Sensor int sensor) {
+        try {
+            return mService.isToggleSensorPrivacyEnabled(toggleType, sensor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Returns whether sensor privacy is currently enabled for a specific sensor.
+     * Combines the state of the SW + HW toggles and returns true if either the
+     * SOFTWARE or the HARDWARE toggles are enabled.
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) {
+    public boolean areAnySensorPrivacyTogglesEnabled(@Sensors.Sensor int sensor) {
         try {
-            return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
+            return mService.isCombinedToggleSensorPrivacyEnabled(sensor);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -524,7 +685,7 @@
     public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
             boolean enable, @UserIdInt int userId) {
         try {
-            mService.setIndividualSensorPrivacy(userId, source, sensor, enable);
+            mService.setToggleSensorPrivacy(userId, source, sensor, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -561,7 +722,7 @@
     public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
             @Sensors.Sensor int sensor, boolean enable, @UserIdInt int userId) {
         try {
-            mService.setIndividualSensorPrivacyForProfileGroup(userId, source, sensor, enable);
+            mService.setToggleSensorPrivacyForProfileGroup(userId, source, sensor, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -592,7 +753,7 @@
     public void suppressSensorPrivacyReminders(int sensor,
             boolean suppress, @UserIdInt int userId) {
         try {
-            mService.suppressIndividualSensorPrivacyReminders(userId, sensor,
+            mService.suppressToggleSensorPrivacyReminders(userId, sensor,
                     token, suppress);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -667,7 +828,8 @@
             if (iListener == null) {
                 iListener = new ISensorPrivacyListener.Stub() {
                     @Override
-                    public void onSensorPrivacyChanged(boolean enabled) {
+                    public void onSensorPrivacyChanged(int toggleType, int sensor,
+                            boolean enabled) {
                         listener.onAllSensorPrivacyChanged(enabled);
                     }
                 };
diff --git a/core/java/android/hardware/SensorPrivacyManagerInternal.java b/core/java/android/hardware/SensorPrivacyManagerInternal.java
index d12e9f8..f4de966 100644
--- a/core/java/android/hardware/SensorPrivacyManagerInternal.java
+++ b/core/java/android/hardware/SensorPrivacyManagerInternal.java
@@ -61,4 +61,9 @@
      */
     public abstract void addSensorPrivacyListenerForAllUsers(int sensor,
             OnUserSensorPrivacyChangedListener listener);
+
+    /**
+     *  Set the HW toggle sensor value based on HW switch states, called from InputManagerService
+     */
+    public abstract void setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable);
 }
diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java
index 693cda7..cd4bf78 100644
--- a/core/java/android/hardware/SyncFence.java
+++ b/core/java/android/hardware/SyncFence.java
@@ -17,6 +17,8 @@
 package android.hardware;
 
 import android.annotation.NonNull;
+import android.media.Image;
+import android.media.ImageWriter;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSync;
 import android.os.Parcel;
@@ -30,14 +32,29 @@
 import java.time.Duration;
 
 /**
- * A SyncFence represents a synchronization primitive which signals when hardware buffers have
- * completed work on a particular resource.
+ * A SyncFence represents a synchronization primitive which signals when hardware units have
+ * completed work on a particular resource. They initially start in an unsignaled state and make
+ * a one-time transition to either a signaled or error state. SyncFences are created by various
+ * device APIs in response to submitting tasks to the device. They cannot be created nor signaled
+ * by userspace. As a result, this means that a SyncFence will make always make forward progress.
+ *
+ * <p>SyncFence's generally come in one of two varieties. "Presentation fences" refer to
+ *  a SyncFence when the writing to a buffer has finished. "Release fences" then refer
+ *  to when the reading from a buffer has finished.</p>
  *
  * <p>For example, a GPU rendering to a framebuffer may generate a synchronization fence,
- * e.g., a VkFence, which signals when rendering has completed.
+ * e.g., an EGLSync or VkFence, which signals when rendering has completed. Once the fence signals,
+ * then the backing storage for the framebuffer may be safely read from, such as for display or
+ * for media encoding. This would be referred to as a "presentation fence."</p>
  *
- * Once the fence signals, then the backing storage for the framebuffer may be safely read from,
- * such as for display or for media encoding.</p>
+ * <p>Similarly when using an {@link android.media.ImageWriter} it is possible that an
+ * {@link android.media.Image} returned by {@link ImageWriter#dequeueInputImage()} may already
+ * have a {@link Image#getFence() fence} set on it. This would be what is referred to as either
+ * a "release fence" or an "acqurie fence" and indicates the fence that the writer must wait
+ * on before writing to the underlying buffer. In the case of ImageWriter this is done
+ * automatically when eg {@link Image#getPlanes()} is called, however when using
+ * {@link Image#getHardwareBuffer()} it is the caller's responsibility to ensure the
+ * release fence has signaled before writing to the buffer.</p>
  *
  * @see android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync)
  * @see android.media.Image#getFence()
@@ -214,6 +231,16 @@
         return CONTENTS_FILE_DESCRIPTOR;
     }
 
+    /** @hide */
+    public Object getLock() {
+        return mCloser;
+    }
+
+    /** @hide */
+    public long getNativeFence() {
+        return mNativePtr;
+    }
+
     /**
      * Flatten this object into a Parcel.
      *
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 32a5ee7..18d86d6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -881,13 +881,13 @@
                 mListener.onAccuracyChanged(t.sensor, t.accuracy);
             }
 
-            // call onSensorDiscontinuity() if the discontinuity counter changed
-            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER
-                    && mListener instanceof SensorEventCallback) {
+            // Indicate if the discontinuity count changed
+            if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER) {
                 final int lastCount = mSensorDiscontinuityCounts.get(handle);
                 final int curCount = Float.floatToIntBits(values[6]);
                 if (lastCount >= 0 && lastCount != curCount) {
-                    ((SensorEventCallback) mListener).onSensorDiscontinuity(t.sensor);
+                    mSensorDiscontinuityCounts.put(handle, curCount);
+                    t.firstEventAfterDiscontinuity = true;
                 }
             }
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c053c92..c341731 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -957,7 +957,7 @@
     public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
             @NonNull VirtualDisplayConfig virtualDisplayConfig,
             @Nullable VirtualDisplay.Callback callback,
-            @NonNull Executor executor) {
+            @Nullable Executor executor) {
         return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
                 virtualDisplayConfig, callback, executor, null);
     }
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 889100d..a62bbf6 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1054,6 +1054,14 @@
         @Nullable private final VirtualDisplay.Callback mCallback;
         @Nullable private final Executor mExecutor;
 
+        /**
+         * Creates a virtual display callback.
+         *
+         * @param callback The callback to call for virtual display events, or {@code null} if the
+         * caller does not wish to receive callback events.
+         * @param executor The executor to call the {@code callback} on. Must not be {@code null} if
+         * the callback is not {@code null}.
+         */
         VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
             mCallback = callback;
             mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2fd79cf..c38a847 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1079,16 +1079,24 @@
     }
 
     /**
-     * Gets the key code produced by the specified location on a US keyboard layout.
-     * Key code as defined in {@link android.view.KeyEvent}.
-     * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
-     * which can alter their key mapping using country specific keyboard layouts.
+     * Gets the {@link android.view.KeyEvent key code} produced by the given location on a reference
+     * QWERTY keyboard layout.
+     * <p>
+     * This API is useful for querying the physical location of keys that change the character
+     * produced based on the current locale and keyboard layout.
+     * <p>
+     * @see InputDevice#getKeyCodeForKeyLocation(int) for examples.
      *
-     * @param deviceId The input device id.
-     * @param locationKeyCode The location of a key on a US keyboard layout.
-     * @return The key code produced when pressing the key at the specified location, given the
-     *         active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
-     *         mapping could not be determined, or if an error occurred.
+     * @param locationKeyCode The location of a key specified as a key code on the QWERTY layout.
+     * This provides a consistent way of referring to the physical location of a key independently
+     * of the current keyboard layout. Also see the
+     * <a href="https://www.w3.org/TR/2017/CR-uievents-code-20170601/#key-alphanumeric-writing-system">
+     * hypothetical keyboard</a> provided by the W3C, which may be helpful for identifying the
+     * physical location of a key.
+     * @return The key code produced by the key at the specified location, given the current
+     * keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the device does not specify
+     * {@link InputDevice#SOURCE_KEYBOARD} or the requested mapping cannot be determined.
+     *
      * @hide
      */
     public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index bf4b514..eed92c1 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -37,6 +37,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.IOException;
+
 /**
  * The SoundTriggerModule provides APIs to control sound models and sound detection
  * on a given sound trigger hardware module.
@@ -137,13 +139,39 @@
             if (model instanceof SoundTrigger.GenericSoundModel) {
                 SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
                         (SoundTrigger.GenericSoundModel) model);
-                soundModelHandle[0] = mService.loadModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.data != null) {
+                        try {
+                            aidlModel.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             if (model instanceof SoundTrigger.KeyphraseSoundModel) {
                 PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
                         (SoundTrigger.KeyphraseSoundModel) model);
-                soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.common.data != null) {
+                        try {
+                            aidlModel.common.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             return SoundTrigger.STATUS_BAD_VALUE;
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index a979725..7b695e7 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -39,13 +39,13 @@
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_ENABLED;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_OVERHEAT;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_CONTAMINANT;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DOCK;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DEBUG;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_ENABLED;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_OVERHEAT;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_CONTAMINANT;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DEBUG;
 
 import android.Manifest;
 import android.annotation.CheckResult;
@@ -400,7 +400,7 @@
     }
 
     /**
-     * Enables Usb data when disabled due to {@link UsbPort#USB_DATA_STATUS_DISABLED_DOCK}
+     * Enables Usb data when disabled due to {@link UsbPort#DATA_STATUS_DISABLED_DOCK}
      *
      * @return {@link #ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS} when request completes successfully or
      *         {@link #ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL} when request fails due to
@@ -421,7 +421,8 @@
                 + " callingUid:" + Binder.getCallingUid());
         UsbPortStatus portStatus = getStatus();
         if (portStatus != null &&
-                !usbDataStatusToString(portStatus.getUsbDataStatus()).contains("disabled-dock")) {
+                (portStatus.getUsbDataStatus() & DATA_STATUS_DISABLED_DOCK) !=
+                 DATA_STATUS_DISABLED_DOCK) {
             return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED;
         }
 
@@ -584,44 +585,43 @@
 
     /** @hide */
     public static String usbDataStatusToString(int usbDataStatus) {
-        switch (usbDataStatus) {
-            case USB_DATA_STATUS_UNKNOWN:
-                return "unknown";
-            case USB_DATA_STATUS_ENABLED:
-                return "enabled";
-            case USB_DATA_STATUS_DISABLED_OVERHEAT:
-                return "disabled-overheat";
-            case USB_DATA_STATUS_DISABLED_CONTAMINANT:
-                return "disabled-contaminant";
-            case USB_DATA_STATUS_DISABLED_DOCK:
-                return "disabled-dock";
-            case USB_DATA_STATUS_DISABLED_FORCE:
-                return "disabled-force";
-            case USB_DATA_STATUS_DISABLED_DEBUG:
-                return "disabled-debug";
-            default:
-                return Integer.toString(usbDataStatus);
-        }
-    }
+        StringBuilder statusString = new StringBuilder();
 
-    /** @hide */
-    public static String usbDataStatusToString(int[] usbDataStatus) {
-        StringBuilder modeString = new StringBuilder();
-        if (usbDataStatus == null) {
+        if (usbDataStatus == DATA_STATUS_UNKNOWN) {
             return "unknown";
         }
-        for (int i = 0; i < usbDataStatus.length; i++) {
-            modeString.append(usbDataStatusToString(usbDataStatus[i]));
-            if (i < usbDataStatus.length - 1) {
-                modeString.append(", ");
-            }
+
+        if ((usbDataStatus & DATA_STATUS_ENABLED) == DATA_STATUS_ENABLED) {
+            return "enabled";
         }
-        return modeString.toString();
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_OVERHEAT) == DATA_STATUS_DISABLED_OVERHEAT) {
+            statusString.append("disabled-overheat, ");
+        }
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_CONTAMINANT)
+                == DATA_STATUS_DISABLED_CONTAMINANT) {
+            statusString.append("disabled-contaminant, ");
+        }
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_DOCK) == DATA_STATUS_DISABLED_DOCK) {
+            statusString.append("disabled-dock, ");
+        }
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_FORCE) == DATA_STATUS_DISABLED_FORCE) {
+            statusString.append("disabled-force, ");
+        }
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_DEBUG) == DATA_STATUS_DISABLED_DEBUG) {
+            statusString.append("disabled-debug, ");
+        }
+
+        return statusString.toString().replaceAll(", $", "");
     }
 
     /** @hide */
-    public static String powerBrickStatusToString(int powerBrickStatus) {
-        switch (powerBrickStatus) {
+    public static String powerBrickConnectionStatusToString(int powerBrickConnectionStatus) {
+        switch (powerBrickConnectionStatus) {
             case POWER_BRICK_STATUS_UNKNOWN:
                 return "unknown";
             case POWER_BRICK_STATUS_CONNECTED:
@@ -629,7 +629,7 @@
             case POWER_BRICK_STATUS_DISCONNECTED:
                 return "disconnected";
             default:
-                return Integer.toString(powerBrickStatus);
+                return Integer.toString(powerBrickConnectionStatus);
         }
     }
 
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index d1f4246..3221ec8 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -44,8 +44,8 @@
     private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
     private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
     private final boolean mPowerTransferLimited;
-    private final @UsbDataStatus int[] mUsbDataStatus;
-    private final @PowerBrickStatus int mPowerBrickStatus;
+    private final @UsbDataStatus int mUsbDataStatus;
+    private final @PowerBrickConnectionStatus int mPowerBrickConnectionStatus;
 
     /**
      * Power role: This USB port does not have a power role.
@@ -198,38 +198,38 @@
     /**
      * USB data status is not known.
      */
-    public static final int USB_DATA_STATUS_UNKNOWN = 0;
+    public static final int DATA_STATUS_UNKNOWN = 0;
 
     /**
      * USB data is enabled.
      */
-    public static final int USB_DATA_STATUS_ENABLED = 1;
+    public static final int DATA_STATUS_ENABLED = 1 << 0;
 
     /**
      * USB data is disabled as the port is too hot.
      */
-    public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
+    public static final int DATA_STATUS_DISABLED_OVERHEAT = 1 << 1;
 
     /**
      * USB data is disabled due to contaminated port.
      */
-    public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
+    public static final int DATA_STATUS_DISABLED_CONTAMINANT = 1 << 2;
 
     /**
      * USB data is disabled due to docking event.
      */
-    public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
+    public static final int DATA_STATUS_DISABLED_DOCK = 1 << 3;
 
     /**
      * USB data is disabled by
      * {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
      */
-    public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
+    public static final int DATA_STATUS_DISABLED_FORCE = 1 << 4;
 
     /**
      * USB data is disabled for debug.
      */
-    public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
+    public static final int DATA_STATUS_DISABLED_DEBUG = 1 << 5;
 
     /**
      * Unknown whether a power brick is connected.
@@ -276,14 +276,14 @@
     @interface UsbPortMode{}
 
     /** @hide */
-    @IntDef(prefix = { "USB_DATA_STATUS_" }, value = {
-            USB_DATA_STATUS_UNKNOWN,
-            USB_DATA_STATUS_ENABLED,
-            USB_DATA_STATUS_DISABLED_OVERHEAT,
-            USB_DATA_STATUS_DISABLED_CONTAMINANT,
-            USB_DATA_STATUS_DISABLED_DOCK,
-            USB_DATA_STATUS_DISABLED_FORCE,
-            USB_DATA_STATUS_DISABLED_DEBUG
+    @IntDef(prefix = { "DATA_STATUS_" }, flag = true, value = {
+            DATA_STATUS_UNKNOWN,
+            DATA_STATUS_ENABLED,
+            DATA_STATUS_DISABLED_OVERHEAT,
+            DATA_STATUS_DISABLED_CONTAMINANT,
+            DATA_STATUS_DISABLED_DOCK,
+            DATA_STATUS_DISABLED_FORCE,
+            DATA_STATUS_DISABLED_DEBUG
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface UsbDataStatus{}
@@ -295,13 +295,14 @@
             POWER_BRICK_STATUS_CONNECTED,
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface PowerBrickStatus{}
+    @interface PowerBrickConnectionStatus{}
 
     /** @hide */
     public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
             int supportedRoleCombinations, int contaminantProtectionStatus,
-            int contaminantDetectionStatus, @UsbDataStatus int[] usbDataStatus,
-            boolean powerTransferLimited, @PowerBrickStatus int powerBrickStatus) {
+            int contaminantDetectionStatus, @UsbDataStatus int usbDataStatus,
+            boolean powerTransferLimited,
+            @PowerBrickConnectionStatus int powerBrickConnectionStatus) {
         mCurrentMode = currentMode;
         mCurrentPowerRole = currentPowerRole;
         mCurrentDataRole = currentDataRole;
@@ -310,7 +311,7 @@
         mContaminantDetectionStatus = contaminantDetectionStatus;
         mUsbDataStatus = usbDataStatus;
         mPowerTransferLimited = powerTransferLimited;
-        mPowerBrickStatus = powerBrickStatus;
+        mPowerBrickConnectionStatus = powerBrickConnectionStatus;
     }
 
     /** @hide */
@@ -323,8 +324,8 @@
         mSupportedRoleCombinations = supportedRoleCombinations;
         mContaminantProtectionStatus = contaminantProtectionStatus;
         mContaminantDetectionStatus = contaminantDetectionStatus;
-        mUsbDataStatus = new int[]{USB_DATA_STATUS_UNKNOWN};
-        mPowerBrickStatus = POWER_BRICK_STATUS_UNKNOWN;
+        mUsbDataStatus = DATA_STATUS_UNKNOWN;
+        mPowerBrickConnectionStatus = POWER_BRICK_STATUS_UNKNOWN;
         mPowerTransferLimited = false;
     }
 
@@ -411,13 +412,13 @@
     /**
      * Returns UsbData status.
      *
-     * @return Current USB data status of the port: {@link #USB_DATA_STATUS_UNKNOWN}
-     *         or {@link #USB_DATA_STATUS_ENABLED} or {@link #USB_DATA_STATUS_DIASBLED_OVERHEAT}
-     *         or {@link #USB_DATA_STATUS_DISABLED_CONTAMINANT}
-     *         or {@link #USB_DATA_STATUS_DISABLED_DOCK} or {@link #USB_DATA_STATUS_DISABLED_FORCE}
-     *         or {@link #USB_DATA_STATUS_DISABLED_DEBUG}
+     * @return Current USB data status of the port with one or more of the following values
+     *         {@link #DATA_STATUS_UNKNOWN}, {@link #DATA_STATUS_ENABLED},
+     *         {@link #DATA_STATUS_DISABLED_OVERHEAT}, {@link #DATA_STATUS_DISABLED_CONTAMINANT},
+     *         {@link #DATA_STATUS_DISABLED_DOCK}, {@link #DATA_STATUS_DISABLED_FORCE},
+     *         {@link #DATA_STATUS_DISABLED_DEBUG}
      */
-    public @UsbDataStatus @Nullable int[] getUsbDataStatus() {
+    public @UsbDataStatus int getUsbDataStatus() {
         return mUsbDataStatus;
     }
 
@@ -432,14 +433,14 @@
     }
 
     /**
-     * Let's the caller know if a power brick is connected to the USB port.
+     * Returns the connection status of the power brick.
      *
      * @return {@link #POWER_BRICK_STATUS_UNKNOWN}
      *         or {@link #POWER_BRICK_STATUS_CONNECTED}
      *         or {@link #POWER_BRICK_STATUS_DISCONNECTED}
      */
-    public @PowerBrickStatus int getPowerBrickStatus() {
-        return mPowerBrickStatus;
+    public @PowerBrickConnectionStatus int getPowerBrickConnectionStatus() {
+        return mPowerBrickConnectionStatus;
     }
 
     @NonNull
@@ -459,8 +460,9 @@
                         + UsbPort.usbDataStatusToString(getUsbDataStatus())
                 + ", isPowerTransferLimited="
                         + isPowerTransferLimited()
-                +", powerBrickStatus="
-                        + UsbPort.powerBrickStatusToString(getPowerBrickStatus())
+                +", powerBrickConnectionStatus="
+                        + UsbPort
+                            .powerBrickConnectionStatusToString(getPowerBrickConnectionStatus())
                 + "}";
     }
 
@@ -477,10 +479,9 @@
         dest.writeInt(mSupportedRoleCombinations);
         dest.writeInt(mContaminantProtectionStatus);
         dest.writeInt(mContaminantDetectionStatus);
-        dest.writeInt(mUsbDataStatus.length);
-        dest.writeIntArray(mUsbDataStatus);
+        dest.writeInt(mUsbDataStatus);
         dest.writeBoolean(mPowerTransferLimited);
-        dest.writeInt(mPowerBrickStatus);
+        dest.writeInt(mPowerBrickConnectionStatus);
     }
 
     public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -493,14 +494,13 @@
             int supportedRoleCombinations = in.readInt();
             int contaminantProtectionStatus = in.readInt();
             int contaminantDetectionStatus = in.readInt();
-            int[] usbDataStatus = new int[in.readInt()];
-            in.readIntArray(usbDataStatus);
+            int usbDataStatus = in.readInt();
             boolean powerTransferLimited = in.readBoolean();
-            int powerBrickStatus = in.readInt();
+            int powerBrickConnectionStatus = in.readInt();
             return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
                     contaminantDetectionStatus, usbDataStatus, powerTransferLimited,
-                    powerBrickStatus);
+                    powerBrickConnectionStatus);
         }
 
         @Override
@@ -508,4 +508,126 @@
             return new UsbPortStatus[size];
         }
     };
+
+    /**
+     * Builder is used to create {@link UsbPortStatus} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private @UsbPortMode int mCurrentMode;
+        private @UsbPowerRole int mCurrentPowerRole;
+        private @UsbDataRole int mCurrentDataRole;
+        private int mSupportedRoleCombinations;
+        private @ContaminantProtectionStatus int mContaminantProtectionStatus;
+        private @ContaminantDetectionStatus int mContaminantDetectionStatus;
+        private boolean mPowerTransferLimited;
+        private @UsbDataStatus int mUsbDataStatus;
+        private @PowerBrickConnectionStatus int mPowerBrickConnectionStatus;
+
+        public Builder() {
+            mCurrentMode = MODE_NONE;
+            mCurrentPowerRole = POWER_ROLE_NONE;
+            mCurrentDataRole = DATA_ROLE_NONE;
+            mContaminantProtectionStatus = CONTAMINANT_PROTECTION_NONE;
+            mContaminantDetectionStatus = CONTAMINANT_DETECTION_NOT_SUPPORTED;
+            mUsbDataStatus = DATA_STATUS_UNKNOWN;
+            mPowerBrickConnectionStatus = POWER_BRICK_STATUS_UNKNOWN;
+        }
+
+        /**
+         * Sets the current mode of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setCurrentMode(@UsbPortMode int currentMode) {
+            mCurrentMode = currentMode;
+            return this;
+        }
+
+        /**
+         * Sets the current power role and data role of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setCurrentRoles(@UsbPowerRole int currentPowerRole,
+                @UsbDataRole int currentDataRole) {
+            mCurrentPowerRole = currentPowerRole;
+            mCurrentDataRole = currentDataRole;
+            return this;
+        }
+
+        /**
+         * Sets supported role combinations of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setSupportedRoleCombinations(int supportedRoleCombinations) {
+            mSupportedRoleCombinations = supportedRoleCombinations;
+            return this;
+        }
+
+        /**
+         * Sets current contaminant status of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setContaminantStatus(
+                @ContaminantProtectionStatus int contaminantProtectionStatus,
+                @ContaminantDetectionStatus int contaminantDetectionStatus) {
+            mContaminantProtectionStatus = contaminantProtectionStatus;
+            mContaminantDetectionStatus = contaminantDetectionStatus;
+            return this;
+        }
+
+        /**
+         * Sets power limit power transfer of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setPowerTransferLimited(boolean powerTransferLimited) {
+            mPowerTransferLimited = powerTransferLimited;
+            return this;
+        }
+
+        /**
+         * Sets the USB data status of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setUsbDataStatus(@UsbDataStatus int usbDataStatus) {
+            mUsbDataStatus = usbDataStatus;
+            return this;
+        }
+
+        /**
+         * Sets the power brick connection status of {@link UsbPortStatus}
+         *
+         * @return Instance of {@link Builder}
+         */
+        @NonNull
+        public Builder setPowerBrickConnectionStatus(
+                @PowerBrickConnectionStatus int powerBrickConnectionStatus) {
+            mPowerBrickConnectionStatus = powerBrickConnectionStatus;
+            return this;
+        }
+
+        /**
+         * Creates the {@link UsbPortStatus} object.
+         */
+        @NonNull
+        public UsbPortStatus build() {
+            UsbPortStatus status = new UsbPortStatus(mCurrentMode, mCurrentPowerRole,
+                    mCurrentDataRole, mSupportedRoleCombinations, mContaminantProtectionStatus,
+                    mContaminantDetectionStatus, mUsbDataStatus, mPowerTransferLimited,
+                    mPowerBrickConnectionStatus);
+            return status;
+        }
+    };
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index adf2759..c3e3180 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -346,7 +346,7 @@
      */
     @AnyThread
     public static boolean canImeRenderGesturalNavButtons() {
-        return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
+        return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false);
     }
 
     /**
@@ -989,24 +989,6 @@
         public void changeInputMethodSubtype(InputMethodSubtype subtype) {
             dispatchOnCurrentInputMethodSubtypeChanged(subtype);
         }
-
-        /**
-         * {@inheritDoc}
-         * @hide
-         */
-        @Override
-        public void setCurrentShowInputToken(IBinder showInputToken) {
-            mCurShowInputToken = showInputToken;
-        }
-
-        /**
-         * {@inheritDoc}
-         * @hide
-         */
-        @Override
-        public void setCurrentHideInputToken(IBinder hideInputToken) {
-            mCurHideInputToken = hideInputToken;
-        }
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 6f4fd76..19fa01d 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -45,7 +45,6 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowInsetsController.Appearance;
-import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
@@ -138,7 +137,7 @@
 
         private boolean mDestroyed = false;
 
-        private boolean mRenderGesturalNavButtons;
+        private boolean mImeDrawsImeNavBar;
 
         @Nullable
         private NavigationBarFrame mNavigationBarFrame;
@@ -185,7 +184,7 @@
         }
 
         private void installNavigationBarFrameIfNecessary() {
-            if (!mRenderGesturalNavButtons) {
+            if (!mImeDrawsImeNavBar) {
                 return;
             }
             if (mNavigationBarFrame != null) {
@@ -253,7 +252,7 @@
         @Override
         public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
                 @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
-            if (!mRenderGesturalNavButtons || mNavigationBarFrame == null
+            if (!mImeDrawsImeNavBar || mNavigationBarFrame == null
                     || mService.isExtractViewShown()) {
                 return;
             }
@@ -360,13 +359,12 @@
             });
         }
 
-        private boolean isGesturalNavigationEnabled() {
+        private boolean imeDrawsImeNavBar() {
             final Resources resources = mService.getResources();
             if (resources == null) {
                 return false;
             }
-            return resources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)
-                    == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+            return resources.getBoolean(com.android.internal.R.bool.config_imeDrawsImeNavBar);
         }
 
         @Override
@@ -381,7 +379,7 @@
             if (mDestroyed) {
                 return;
             }
-            mRenderGesturalNavButtons = isGesturalNavigationEnabled();
+            mImeDrawsImeNavBar = imeDrawsImeNavBar();
             if (mSystemOverlayChangedReceiver == null) {
                 final IntentFilter intentFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
                 intentFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
@@ -392,8 +390,8 @@
                         if (mDestroyed) {
                             return;
                         }
-                        mRenderGesturalNavButtons = isGesturalNavigationEnabled();
-                        if (mRenderGesturalNavButtons) {
+                        mImeDrawsImeNavBar = imeDrawsImeNavBar();
+                        if (mImeDrawsImeNavBar) {
                             installNavigationBarFrameIfNecessary();
                         } else {
                             uninstallNavigationBarFrameIfNecessary();
@@ -423,7 +421,7 @@
 
         @Override
         public void onWindowShown() {
-            if (mDestroyed || !mRenderGesturalNavButtons || mNavigationBarFrame == null) {
+            if (mDestroyed || !mImeDrawsImeNavBar || mNavigationBarFrame == null) {
                 return;
             }
             final Insets systemInsets = getSystemInsets();
@@ -546,7 +544,7 @@
 
         @Override
         public String toDebugString() {
-            return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
+            return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar
                     + " mNavigationBarFrame=" + mNavigationBarFrame
                     + " mShouldShowImeSwitcherWhenImeIsShown="
                     + mShouldShowImeSwitcherWhenImeIsShown
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 0a9fe90..9a780c8 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -84,13 +84,6 @@
     public static final String EXTRA_SERVICE_COMPONENT = "component";
 
     /**
-     * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}.
-     *
-     * @see #ACTION_CHANGE_DEFAULT
-     */
-    public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
-
-    /**
      * Category used for NFC payment services.
      */
     public static final String CATEGORY_PAYMENT = "payment";
diff --git a/core/java/android/os/BadTypeParcelableException.java b/core/java/android/os/BadTypeParcelableException.java
new file mode 100644
index 0000000..2ca3bd2
--- /dev/null
+++ b/core/java/android/os/BadTypeParcelableException.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 android.os;
+
+/** Used by Parcel to signal that the type on the payload was not expected by the caller. */
+class BadTypeParcelableException extends BadParcelableException {
+    BadTypeParcelableException(String msg) {
+        super(msg);
+    }
+    BadTypeParcelableException(Exception cause) {
+        super(cause);
+    }
+    BadTypeParcelableException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 244335d..45812e5 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -31,7 +33,7 @@
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Set;
-import java.util.function.Function;
+import java.util.function.BiFunction;
 
 /**
  * A mapping from String keys to values of various types. In most cases, you
@@ -254,8 +256,8 @@
         }
         try {
             return getValueAt(0, String.class);
-        } catch (ClassCastException | BadParcelableException e) {
-            typeWarning("getPairValue()", /* value */ null, "String", e);
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning("getPairValue()", "String", e);
             return null;
         }
     }
@@ -320,28 +322,46 @@
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
      *
+     * @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in
+     *      other deprecated APIs.
+     *
      * @hide
      */
+    @Deprecated
+    @Nullable
     final Object getValue(String key) {
         return getValue(key, /* clazz */ null);
     }
 
+    /** Same as {@link #getValue(String, Class, Class[])} with no item types. */
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return getValue(key, clazz, (Class<?>[]) null);
+    }
+
     /**
-     * Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
+     * Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code
      * null} for no type check).
      *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
+     *
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
      *
      * @hide
      */
-    final <T> T getValue(String key, @Nullable Class<T> clazz) {
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
         int i = mMap.indexOfKey(key);
-        return (i >= 0) ? getValueAt(i, clazz) : null;
+        return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null;
     }
 
     /**
-     * Returns the value for a certain position in the array map.
+     * Returns the value for a certain position in the array map for expected return type {@code
+     * clazz} (or pass {@code null} for no type check).
+     *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
      *
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
@@ -349,11 +369,12 @@
      * @hide
      */
     @SuppressWarnings("unchecked")
-    final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
+    @Nullable
+    final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
         Object object = mMap.valueAt(i);
-        if (object instanceof Function<?, ?>) {
+        if (object instanceof BiFunction<?, ?, ?>) {
             try {
-                object = ((Function<Class<?>, ?>) object).apply(clazz);
+                object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes);
             } catch (BadParcelableException e) {
                 if (sShouldDefuse) {
                     Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -615,7 +636,11 @@
      *
      * @param key a String key
      * @return an Object, or null
+     *
+     * @deprecated Use the type-safe specific APIs depending on the type of the item to be
+     *      retrieved, eg. {@link #getString(String)}.
      */
+    @Deprecated
     @Nullable
     public Object get(String key) {
         unparcel();
@@ -623,6 +648,32 @@
     }
 
     /**
+     * Returns the object of type {@code clazz} for the given {@code key}, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p>Use the more specific APIs where possible, especially in the case of containers such as
+     * lists, since those APIs allow you to specify the type of the items.
+     *
+     * @param key String key
+     * @param clazz The type of the object expected
+     * @return an Object, or null
+     */
+    @Nullable
+    <T> T get(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName(), e);
+            return null;
+        }
+    }
+
+    /**
      * Removes any entry with the given key from the mapping of this Bundle.
      *
      * @param key a String key
@@ -1006,7 +1057,7 @@
             sb.append(" but value was a ");
             sb.append(value.getClass().getName());
         } else {
-            sb.append(" but value was of a different type ");
+            sb.append(" but value was of a different type");
         }
         sb.append(".  The default value ");
         sb.append(defaultValue);
@@ -1019,6 +1070,10 @@
         typeWarning(key, value, className, "<null>", e);
     }
 
+    void typeWarning(String key, String className, RuntimeException e) {
+        typeWarning(key, /* value */ null, className, "<null>", e);
+    }
+
     /**
      * Returns the value associated with the given key, or defaultValue if
      * no mapping of the desired type exists for the given key.
@@ -1358,7 +1413,11 @@
      *
      * @param key a String, or null
      * @return a Serializable value, or null
+     *
+     * @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in
+     *      other deprecated APIs.
      */
+    @Deprecated
     @Nullable
     Serializable getSerializable(@Nullable String key) {
         unparcel();
@@ -1375,6 +1434,36 @@
     }
 
     /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * @param key a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    <T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) {
+        return get(key, clazz);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    <T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, ArrayList.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
@@ -1384,17 +1473,7 @@
      */
     @Nullable
     ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<Integer>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<Integer>", e);
-            return null;
-        }
+        return getArrayList(key, Integer.class);
     }
 
     /**
@@ -1407,17 +1486,7 @@
      */
     @Nullable
     ArrayList<String> getStringArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<String>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<String>", e);
-            return null;
-        }
+        return getArrayList(key, String.class);
     }
 
     /**
@@ -1430,17 +1499,7 @@
      */
     @Nullable
     ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<CharSequence>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<CharSequence>", e);
-            return null;
-        }
+        return getArrayList(key, CharSequence.class);
     }
 
     /**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 2b13f20..edbbb59 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 import android.util.Size;
@@ -876,7 +877,7 @@
     @Nullable
     public Bundle getBundle(@Nullable String key) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return null;
         }
@@ -899,7 +900,11 @@
      *
      * @param key a String, or {@code null}
      * @return a Parcelable value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> T getParcelable(@Nullable String key) {
         unparcel();
@@ -916,30 +921,28 @@
     }
 
     /**
-     * Returns the value associated with the given key, or {@code null} if
-     * no mapping of the desired type exists for the given key or a {@code null}
-     * value is explicitly associated with the key.
+     * Returns the value associated with the given key or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
      *
      * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
      * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
      * Otherwise, this method might throw an exception or return {@code null}.
      *
      * @param key a String, or {@code null}
-     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @param clazz The type of the object expected
      * @return a Parcelable value, or {@code null}
-     *
-     * @hide
      */
     @SuppressWarnings("unchecked")
     @Nullable
     public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
-        unparcel();
-        try {
-            return getValue(key, requireNonNull(clazz));
-        } catch (ClassCastException | BadParcelableException e) {
-            typeWarning(key, /* value */ null, "Parcelable", e);
-            return null;
-        }
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return get(key, clazz);
     }
 
     /**
@@ -953,7 +956,11 @@
      *
      * @param key a String, or {@code null}
      * @return a Parcelable[] value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public Parcelable[] getParcelableArray(@Nullable String key) {
         unparcel();
@@ -970,6 +977,39 @@
     }
 
     /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @param clazz The type of the items inside the array
+     * @return a Parcelable[] value, or {@code null}
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            // In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting.
+            return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName() + "[]", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or {@code null} if
      * no mapping of the desired type exists for the given key or a {@code null}
      * value is explicitly associated with the key.
@@ -980,7 +1020,11 @@
      *
      * @param key a String, or {@code null}
      * @return an ArrayList<T> value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
         unparcel();
@@ -997,14 +1041,43 @@
     }
 
     /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key   a String, or {@code null}
+     * @param clazz The type of the items inside the array list
+     * @return an ArrayList<T> value, or {@code null}
+     */
+    @SuppressLint("NullableCollection")
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> ArrayList<T> getParcelableArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return getArrayList(key, clazz);
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     *
      * @return a SparseArray of T values, or null
+     *
+     * @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
         unparcel();
@@ -1021,13 +1094,44 @@
     }
 
     /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * @param key a String, or null
+     * @return a SparseArray of T values, or null
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key,
+            @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
      * @return a Serializable value, or null
+     *
+     * @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Override
     @Nullable
     public Serializable getSerializable(@Nullable String key) {
@@ -1035,6 +1139,24 @@
     }
 
     /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * @param key   a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    public <T extends Serializable> T getSerializable(@Nullable String key,
+            @NonNull Class<T> clazz) {
+        return super.getSerializable(key, requireNonNull(clazz));
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index ae92353..09cfb6e 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.IntDef;
@@ -65,6 +67,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.function.Supplier;
@@ -287,26 +290,26 @@
     private static final int VAL_NULL = -1;
     private static final int VAL_STRING = 0;
     private static final int VAL_INTEGER = 1;
-    private static final int VAL_MAP = 2;
+    private static final int VAL_MAP = 2; // length-prefixed
     private static final int VAL_BUNDLE = 3;
-    private static final int VAL_PARCELABLE = 4;
+    private static final int VAL_PARCELABLE = 4; // length-prefixed
     private static final int VAL_SHORT = 5;
     private static final int VAL_LONG = 6;
     private static final int VAL_FLOAT = 7;
     private static final int VAL_DOUBLE = 8;
     private static final int VAL_BOOLEAN = 9;
     private static final int VAL_CHARSEQUENCE = 10;
-    private static final int VAL_LIST  = 11;
-    private static final int VAL_SPARSEARRAY = 12;
+    private static final int VAL_LIST  = 11; // length-prefixed
+    private static final int VAL_SPARSEARRAY = 12; // length-prefixed
     private static final int VAL_BYTEARRAY = 13;
     private static final int VAL_STRINGARRAY = 14;
     private static final int VAL_IBINDER = 15;
-    private static final int VAL_PARCELABLEARRAY = 16;
-    private static final int VAL_OBJECTARRAY = 17;
+    private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
+    private static final int VAL_OBJECTARRAY = 17; // length-prefixed
     private static final int VAL_INTARRAY = 18;
     private static final int VAL_LONGARRAY = 19;
     private static final int VAL_BYTE = 20;
-    private static final int VAL_SERIALIZABLE = 21;
+    private static final int VAL_SERIALIZABLE = 21; // length-prefixed
     private static final int VAL_SPARSEBOOLEANARRAY = 22;
     private static final int VAL_BOOLEANARRAY = 23;
     private static final int VAL_CHARSEQUENCEARRAY = 24;
@@ -3179,8 +3182,7 @@
      */
     @Deprecated
     public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
-        int n = readInt();
-        readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+        readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
     /**
@@ -3195,8 +3197,7 @@
             @NonNull Class<V> clazzValue) {
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
-        int n = readInt();
-        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+        readMapInternal(outVal, loader, clazzKey, clazzValue);
     }
 
     /**
@@ -3245,13 +3246,7 @@
     @Deprecated
     @Nullable
     public HashMap readHashMap(@Nullable ClassLoader loader) {
-        int n = readInt();
-        if (n < 0) {
-            return null;
-        }
-        HashMap m = new HashMap(n);
-        readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
-        return m;
+        return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
     /**
@@ -3267,13 +3262,7 @@
             @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
-        int n = readInt();
-        if (n < 0) {
-            return null;
-        }
-        HashMap<K, V> map = new HashMap<>(n);
-        readMapInternal(map, n, loader, clazzKey, clazzValue);
-        return map;
+        return readHashMapInternal(loader, clazzKey, clazzValue);
     }
 
     /**
@@ -4296,16 +4285,17 @@
 
 
     /**
-     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @see #readValue(int, ClassLoader, Class, Class[])
      */
     @Nullable
-    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
         int type = readInt();
         final T object;
         if (isLengthPrefixed(type)) {
             int length = readInt();
             int start = dataPosition();
-            object = readValue(type, loader, clazz);
+            object = readValue(type, loader, clazz, itemTypes);
             int actual = dataPosition() - start;
             if (actual != length) {
                 Slog.wtfStack(TAG,
@@ -4313,25 +4303,26 @@
                                 + "  consumed " + actual + " bytes, but " + length + " expected.");
             }
         } else {
-            object = readValue(type, loader, clazz);
+            object = readValue(type, loader, clazz, itemTypes);
         }
         return object;
     }
 
     /**
-     * This will return a {@link Function} for length-prefixed types that deserializes the object
-     * when {@link Function#apply} is called with the expected class of the return object (or {@code
-     * null} for no type check), for other types it will return the object itself.
+     * This will return a {@link BiFunction} for length-prefixed types that deserializes the object
+     * when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link
+     * #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it
+     * will return the object itself.
      *
-     * <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
-     * you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+     * <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you
+     * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
      * synchronization attempts are made.
      *
      * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
      * function objects are equal if either of the following is true:
      * <ul>
-     *   <li>{@link Function#apply} has been called on both and both objects returned are equal.
-     *   <li>{@link Function#apply} hasn't been called on either one and everything below is true:
+     *   <li>{@link BiFunction#apply} has been called on both and both objects returned are equal.
+     *   <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true:
      *   <ul>
      *       <li>The {@code loader} parameters used to retrieve each are equal.
      *       <li>They both have the same type.
@@ -4358,7 +4349,7 @@
     }
 
 
-    private static final class LazyValue implements Function<Class<?>, Object> {
+    private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
         /**
          *                      |   4B   |   4B   |
          * mSource = Parcel{... |  type  | length | object | ...}
@@ -4390,7 +4381,7 @@
         }
 
         @Override
-        public Object apply(@Nullable Class<?> clazz) {
+        public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
             Parcel source = mSource;
             if (source != null) {
                 synchronized (source) {
@@ -4399,7 +4390,7 @@
                         int restore = source.dataPosition();
                         try {
                             source.setDataPosition(mPosition);
-                            mObject = source.readValue(mLoader, clazz);
+                            mObject = source.readValue(mLoader, clazz, itemTypes);
                         } finally {
                             source.setDataPosition(restore);
                         }
@@ -4479,14 +4470,25 @@
         }
     }
 
+    /** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return readValue(type, loader, clazz, (Class<?>[]) null);
+    }
+
     /**
      * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
      * type first.
+     *
      * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @param itemTypes If the value is a container, these represent the item types (eg. for a list
+     *                  it's the item type, for a map, it's the key type, followed by the value
+     *                  type).
      */
     @SuppressWarnings("unchecked")
     @Nullable
-    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
         final Object object;
         switch (type) {
             case VAL_NULL:
@@ -4502,7 +4504,11 @@
                 break;
 
             case VAL_MAP:
-                object = readHashMap(loader);
+                checkTypeToUnparcel(clazz, HashMap.class);
+                Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0);
+                Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1);
+                checkArgument((keyType == null) == (valueType == null));
+                object = readHashMapInternal(loader, keyType, valueType);
                 break;
 
             case VAL_PARCELABLE:
@@ -4533,10 +4539,12 @@
                 object = readCharSequence();
                 break;
 
-            case VAL_LIST:
-                object = readArrayList(loader);
+            case VAL_LIST: {
+                checkTypeToUnparcel(clazz, ArrayList.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readArrayListInternal(loader, itemType);
                 break;
-
+            }
             case VAL_BOOLEANARRAY:
                 object = createBooleanArray();
                 break;
@@ -4557,10 +4565,12 @@
                 object = readStrongBinder();
                 break;
 
-            case VAL_OBJECTARRAY:
-                object = readArray(loader);
+            case VAL_OBJECTARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class);
+                object = readArrayInternal(loader, itemType);
                 break;
-
+            }
             case VAL_INTARRAY:
                 object = createIntArray();
                 break;
@@ -4577,14 +4587,18 @@
                 object = readSerializableInternal(loader, clazz);
                 break;
 
-            case VAL_PARCELABLEARRAY:
-                object = readParcelableArray(loader);
+            case VAL_PARCELABLEARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class);
+                object = readParcelableArrayInternal(loader, itemType);
                 break;
-
-            case VAL_SPARSEARRAY:
-                object = readSparseArray(loader);
+            }
+            case VAL_SPARSEARRAY: {
+                checkTypeToUnparcel(clazz, SparseArray.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readSparseArrayInternal(loader, itemType);
                 break;
-
+            }
             case VAL_SPARSEBOOLEANARRAY:
                 object = readSparseBooleanArray();
                 break;
@@ -4632,7 +4646,7 @@
                             + " at offset " + off);
         }
         if (object != null && clazz != null && !clazz.isInstance(object)) {
-            throw new BadParcelableException("Unparcelled object " + object
+            throw new BadTypeParcelableException("Unparcelled object " + object
                     + " is not an instance of required class " + clazz.getName()
                     + " provided in the parameter");
         }
@@ -4659,6 +4673,38 @@
     }
 
     /**
+     * Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of
+     * {@code requiredArrayType}.
+     */
+    private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType,
+            Class<?> componentTypeToUnparcel) {
+        if (requiredArrayType != null) {
+            // In Java 12, we could use componentTypeToUnparcel.arrayType() for the check
+            Class<?> requiredComponentType = requiredArrayType.getComponentType();
+            if (requiredComponentType == null) {
+                throw new BadTypeParcelableException(
+                        "About to unparcel an array but type "
+                                + requiredArrayType.getCanonicalName()
+                                + " required by caller is not an array.");
+            }
+            checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel);
+        }
+    }
+
+    /**
+     * Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code
+     * requiredType} is not {@code null}.
+     */
+    private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) {
+        if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) {
+            throw new BadTypeParcelableException(
+                    "About to unparcel a " + typeToUnparcel.getCanonicalName()
+                            + ", which is not a subtype of type " + requiredType.getCanonicalName()
+                            + " required by caller.");
+        }
+    }
+
+    /**
      * Read and return a new Parcelable from the parcel.  The given class loader
      * will be used to load any enclosed Parcelables.  If it is null, the default
      * class loader will be used.
@@ -4788,7 +4834,7 @@
             if (clazz != null) {
                 Class<?> parcelableClass = creator.getClass().getEnclosingClass();
                 if (!clazz.isAssignableFrom(parcelableClass)) {
-                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
                             + "a subclass of required class " + clazz.getName()
                             + " provided in the parameter");
                 }
@@ -4811,7 +4857,7 @@
             }
             if (clazz != null) {
                 if (!clazz.isAssignableFrom(parcelableClass)) {
-                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
                             + "a subclass of required class " + clazz.getName()
                             + " provided in the parameter");
                 }
@@ -4872,15 +4918,7 @@
     @Deprecated
     @Nullable
     public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
-        int N = readInt();
-        if (N < 0) {
-            return null;
-        }
-        Parcelable[] p = new Parcelable[N];
-        for (int i = 0; i < N; i++) {
-            p[i] = readParcelable(loader);
-        }
-        return p;
+        return readParcelableArrayInternal(loader, /* clazz */ null);
     }
 
     /**
@@ -4892,14 +4930,20 @@
      * trying to instantiate an element.
      */
     @SuppressLint({"ArrayReturn", "NullableCollection"})
-    @SuppressWarnings("unchecked")
     @Nullable
     public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        return readParcelableArrayInternal(loader, requireNonNull(clazz));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader,
+            @Nullable Class<T> clazz) {
         int n = readInt();
         if (n < 0) {
             return null;
         }
-        T[] p = (T[]) Array.newInstance(clazz, n);
+        T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
         for (int i = 0; i < n; i++) {
             p[i] = readParcelableInternal(loader, clazz);
         }
@@ -4962,7 +5006,7 @@
                 // the class the same way as ObjectInputStream, using the provided classloader.
                 Class<?> cl = Class.forName(name, false, loader);
                 if (!clazz.isAssignableFrom(cl)) {
-                    throw new BadParcelableException("Serializable object "
+                    throw new BadTypeParcelableException("Serializable object "
                             + cl.getName() + " is not a subclass of required class "
                             + clazz.getName() + " provided in the parameter");
                 }
@@ -4987,7 +5031,7 @@
                 // the deserialized object, as we cannot resolve the class the same way as
                 // ObjectInputStream.
                 if (!clazz.isAssignableFrom(object.getClass())) {
-                    throw new BadParcelableException("Serializable object "
+                    throw new BadTypeParcelableException("Serializable object "
                             + object.getClass().getName() + " is not a subclass of required class "
                             + clazz.getName() + " provided in the parameter");
                 }
@@ -5097,7 +5141,26 @@
         readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
     }
 
-    /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+    @Nullable
+    private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader,
+            @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        HashMap<K, V> map = new HashMap<>(n);
+        readMapInternal(map, n, loader, clazzKey, clazzValue);
+        return map;
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal,
+            @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+            @Nullable Class<V> clazzValue) {
+        int n = readInt();
+        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
             @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
             @Nullable Class<V> clazzValue) {
         while (n > 0) {
@@ -5108,7 +5171,7 @@
         }
     }
 
-    /* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
+    private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
             int size, @Nullable ClassLoader loader) {
         readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
     }
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index b9252d6..7379443 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -104,6 +104,8 @@
 public final class SystemClock {
     private static final String TAG = "SystemClock";
 
+    private static volatile IAlarmManager sIAlarmManager;
+
     /**
      * This class is uninstantiable.
      */
@@ -151,8 +153,7 @@
      * @return if the clock was successfully set to the specified time.
      */
     public static boolean setCurrentTimeMillis(long millis) {
-        final IAlarmManager mgr = IAlarmManager.Stub
-                .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        final IAlarmManager mgr = getIAlarmManager();
         if (mgr == null) {
             Slog.e(TAG, "Unable to set RTC: mgr == null");
             return false;
@@ -280,8 +281,7 @@
      * @hide
      */
     public static long currentNetworkTimeMillis() {
-        final IAlarmManager mgr = IAlarmManager.Stub
-                .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        final IAlarmManager mgr = getIAlarmManager();
         if (mgr != null) {
             try {
                 return mgr.currentNetworkTimeMillis();
@@ -296,6 +296,14 @@
         }
     }
 
+    private static IAlarmManager getIAlarmManager() {
+        if (sIAlarmManager == null) {
+            sIAlarmManager = IAlarmManager.Stub
+                    .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        }
+        return sIAlarmManager;
+    }
+
     /**
      * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
      * synchronized using a remote network source outside the device.
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6b869f1..043e688 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -106,6 +106,9 @@
     /** @hide */
     public static final long TRACE_TAG_RRO = 1L << 26;
     /** @hide */
+    public static final long TRACE_TAG_THERMAL = 1L << 27;
+    /** @hide */
+
     public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index d223a19..642c618 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -27,7 +27,7 @@
 import java.util.Objects;
 
 /**
- * A class to encapsulate a collection of attributes describing information about a vibration
+ * Encapsulates a collection of attributes describing information about a vibration.
  */
 public final class VibrationAttributes implements Parcelable {
     private static final String TAG = "VibrationAttributes";
@@ -174,7 +174,7 @@
             FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 
     /** Creates a new {@link VibrationAttributes} instance with given usage. */
-    public static @NonNull VibrationAttributes createForUsage(int usage) {
+    public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
         return new VibrationAttributes.Builder().setUsage(usage).build();
     }
 
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 21c6487..237f6ed 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -48,7 +48,7 @@
 /**
  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
  *
- * These effects may be any number of things, from single shot vibrations to complex waveforms.
+ * <p>These effects may be any number of things, from single shot vibrations to complex waveforms.
  */
 public abstract class VibrationEffect implements Parcelable {
     // Stevens' coefficient to scale the perceived vibration intensity.
@@ -110,7 +110,7 @@
     /**
      * A texture effect meant to replicate soft ticks.
      *
-     * Unlike normal effects, texture effects are meant to be called repeatedly, generally in
+     * <p>Unlike normal effects, texture effects are meant to be called repeatedly, generally in
      * response to some motion, in order to replicate the feeling of some texture underneath the
      * user's fingers.
      *
@@ -175,7 +175,7 @@
     /**
      * Create a one shot vibration.
      *
-     * One shot vibrations will vibrate constantly for the specified period of time at the
+     * <p>One shot vibrations will vibrate constantly for the specified period of time at the
      * specified amplitude, and then stop.
      *
      * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
@@ -269,13 +269,13 @@
     /**
      * Create a predefined vibration effect.
      *
-     * Predefined effects are a set of common vibration effects that should be identical, regardless
-     * of the app they come from, in order to provide a cohesive experience for users across
-     * the entire device. They also may be custom tailored to the device hardware in order to
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
      * provide a better experience than you could otherwise build using the generic building
      * blocks.
      *
-     * This will fallback to a generic pattern if one exists and there does not exist a
+     * <p>This will fallback to a generic pattern if one exists and there does not exist a
      * hardware-specific implementation of the effect.
      *
      * @param effectId The ID of the effect to perform:
@@ -291,13 +291,13 @@
     /**
      * Get a predefined vibration effect.
      *
-     * Predefined effects are a set of common vibration effects that should be identical, regardless
-     * of the app they come from, in order to provide a cohesive experience for users across
-     * the entire device. They also may be custom tailored to the device hardware in order to
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
      * provide a better experience than you could otherwise build using the generic building
      * blocks.
      *
-     * This will fallback to a generic pattern if one exists and there does not exist a
+     * <p>This will fallback to a generic pattern if one exists and there does not exist a
      * hardware-specific implementation of the effect.
      *
      * @param effectId The ID of the effect to perform:
@@ -314,16 +314,16 @@
     /**
      * Get a predefined vibration effect.
      *
-     * Predefined effects are a set of common vibration effects that should be identical, regardless
-     * of the app they come from, in order to provide a cohesive experience for users across
-     * the entire device. They also may be custom tailored to the device hardware in order to
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
      * provide a better experience than you could otherwise build using the generic building
      * blocks.
      *
-     * Some effects you may only want to play if there's a hardware specific implementation because
-     * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
-     * parameter allows you to decide whether you want to fallback to the generic implementation or
-     * only play if there's a tuned, hardware specific one available.
+     * <p>Some effects you may only want to play if there's a hardware specific implementation
+     * because they may, for example, be too disruptive to the user without tuning. The
+     * {@code fallback} parameter allows you to decide whether you want to fallback to the generic
+     * implementation or only play if there's a tuned, hardware specific one available.
      *
      * @param effectId The ID of the effect to perform:
      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
@@ -344,9 +344,9 @@
     /**
      * Get a predefined vibration effect associated with a given URI.
      *
-     * Predefined effects are a set of common vibration effects that should be identical, regardless
-     * of the app they come from, in order to provide a cohesive experience for users across
-     * the entire device. They also may be custom tailored to the device hardware in order to
+     * <p>Predefined effects are a set of common vibration effects that should be identical,
+     * regardless of the app they come from, in order to provide a cohesive experience for users
+     * across the entire device. They also may be custom tailored to the device hardware in order to
      * provide a better experience than you could otherwise build using the generic building
      * blocks.
      *
@@ -474,7 +474,7 @@
     /**
      * Gets the estimated duration of the vibration in milliseconds.
      *
-     * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
+     * <p>For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
      * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
      * the length is device and potentially run-time dependent), this returns -1.
      *
@@ -817,28 +817,26 @@
      * effect that grows in intensity and then dies off, with a longer rising portion for emphasis
      * and an extra tick 100ms after:
      *
-     * <code>
-     * VibrationEffect effect = VibrationEffect.startComposition()
+     * <pre>
+     * {@code VibrationEffect effect = VibrationEffect.startComposition()
      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
-     *     .compose();
-     * </code>
+     *     .compose();}</pre>
      *
      * <p>Composition elements can also be {@link VibrationEffect} instances, including other
      * compositions, and off durations, which are periods of time when the vibrator will be
      * turned off. Here is an example of a composition that "warms up" with a light tap,
      * a stronger double tap, then repeats a vibration pattern indefinitely:
      *
-     * <code>
-     * VibrationEffect repeatingEffect = VibrationEffect.startComposition()
+     * <pre>
+     * {@code VibrationEffect repeatingEffect = VibrationEffect.startComposition()
      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
      *     .addOffDuration(Duration.ofMillis(10))
      *     .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
      *     .addOffDuration(Duration.ofMillis(50))
      *     .addEffect(VibrationEffect.createWaveform(pattern, repeatIndex))
-     *     .compose();
-     * </code>
+     *     .compose();}</pre>
      *
      * <p>When choosing to play a composed effect, you should check that individual components are
      * supported by the device by using the appropriate vibrator method:
@@ -932,7 +930,7 @@
 
         /**
          * Adds a time duration to the current composition, during which the vibrator will be
-         * turned off
+         * turned off.
          *
          * @param duration The length of time the vibrator should be off. Value must be non-negative
          *                 and will be truncated to milliseconds.
@@ -1004,7 +1002,7 @@
         /**
          * Add a haptic primitive to the end of the current composition.
          *
-         * Similar to {@link #addPrimitive(int, float, int)}, but with no delay and a
+         * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay and a
          * default scale applied.
          *
          * @param primitiveId The primitive to add
@@ -1021,7 +1019,7 @@
         /**
          * Add a haptic primitive to the end of the current composition.
          *
-         * Similar to {@link #addPrimitive(int, float, int)}, but with no delay.
+         * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay.
          *
          * @param primitiveId The primitive to add
          * @param scale The scale to apply to the intensity of the primitive.
@@ -1081,9 +1079,9 @@
         /**
          * Compose all of the added primitives together into a single {@link VibrationEffect}.
          *
-         * The {@link Composition} object is still valid after this call, so you can continue adding
-         * more primitives to it and generating more {@link VibrationEffect}s by calling this method
-         * again.
+         * <p>The {@link Composition} object is still valid after this call, so you can continue
+         * adding more primitives to it and generating more {@link VibrationEffect}s by calling this
+         * method again.
          *
          * @return The {@link VibrationEffect} resulting from the composition of the primitives.
          */
@@ -1099,7 +1097,7 @@
         }
 
         /**
-         * Convert the primitive ID to a human readable string for debugging
+         * Convert the primitive ID to a human readable string for debugging.
          * @param id The ID to convert
          * @return The ID in a human readable format.
          * @hide
@@ -1139,16 +1137,15 @@
      * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms
      * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms:
      *
-     * <code>
-     * import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
+     * <pre>
+     * {@code import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
      * import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
      *
      * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
      *     .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
      *     .addSustain(Duration.ofMillis(200))
      *     .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
-     *     .build();
-     * </code>
+     *     .build();}</pre>
      *
      * <p>The initial state of the waveform can be set via
      * {@link VibrationEffect#startWaveform(VibrationParameter)} or
@@ -1169,8 +1166,8 @@
      * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a
      * repeated beating effect with a rise that stretches out and a sharp finish.
      *
-     * <code>
-     * VibrationEffect patternToBeRepeated = VibrationEffect.startWaveform(targetAmplitude(0.2f))
+     * <pre>
+     * {@code VibrationEffect patternToRepeat = VibrationEffect.startWaveform(targetAmplitude(0.2f))
      *     .addSustain(Duration.ofMillis(10))
      *     .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
      *     .addSustain(Duration.ofMillis(30))
@@ -1182,16 +1179,15 @@
      * VibrationEffect effect = VibrationEffect.startComposition()
      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
      *     .addOffDuration(Duration.ofMillis(20))
-     *     .repeatEffectIndefinitely(patternToBeRepeated)
-     *     .compose();
-     * </code>
+     *     .repeatEffectIndefinitely(patternToRepeat)
+     *     .compose();}</pre>
      *
      * <p>The amplitude step waveforms that can be created via
      * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with
      * {@link WaveformBuilder} by adding zero duration transitions:
      *
-     * <code>
-     * // These two effects are the same
+     * <pre>
+     * {@code // These two effects are the same
      * VibrationEffect waveform = VibrationEffect.createWaveform(
      *     new long[] { 10, 20, 30 },  // timings in milliseconds
      *     new int[] { 51, 102, 204 }, // amplitudes in [0,255]
@@ -1203,8 +1199,7 @@
      *     .addSustain(Duration.ofMillis(20))
      *     .addTransition(Duration.ZERO, targetAmplitude(0.8f))
      *     .addSustain(Duration.ofMillis(30))
-     *     .build();
-     * </code>
+     *     .build();}</pre>
      *
      * @see VibrationEffect#startWaveform
      */
@@ -1307,7 +1302,7 @@
         /**
          * Build the waveform as a single {@link VibrationEffect}.
          *
-         * The {@link WaveformBuilder} object is still valid after this call, so you can
+         * <p>The {@link WaveformBuilder} object is still valid after this call, so you can
          * continue adding more primitives to it and generating more {@link VibrationEffect}s by
          * calling this method again.
          *
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 78f1cb1..7f0d634 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -83,7 +83,7 @@
     /**
      * Vibration effect support: unknown
      *
-     * The hardware doesn't report it's supported effects, so we can't determine whether the
+     * <p>The hardware doesn't report its supported effects, so we can't determine whether the
      * effect is supported or not.
      */
     public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0;
@@ -91,14 +91,14 @@
     /**
      * Vibration effect support: supported
      *
-     * This effect is supported by the underlying hardware.
+     * <p>This effect is supported by the underlying hardware.
      */
     public static final int VIBRATION_EFFECT_SUPPORT_YES = 1;
 
     /**
      * Vibration effect support: unsupported
      *
-     * This effect is <b>not</b> natively supported by the underlying hardware, although
+     * <p>This effect is <b>not</b> natively supported by the underlying hardware, although
      * the system may still play a fallback vibration.
      */
     public static final int VIBRATION_EFFECT_SUPPORT_NO = 2;
@@ -317,7 +317,7 @@
     /**
      * Vibrate constantly for the specified period of time.
      *
-     * <p>The app should be in foreground for the vibration to happen.</p>
+     * <p>The app should be in the foreground for the vibration to happen.</p>
      *
      * @param milliseconds The number of milliseconds to vibrate.
      * @deprecated Use {@link #vibrate(VibrationEffect)} instead.
@@ -331,7 +331,7 @@
     /**
      * Vibrate constantly for the specified period of time.
      *
-     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
      * specify a ringtone, notification or alarm usage in order to vibrate.</p>
      *
      * @param milliseconds The number of milliseconds to vibrate.
@@ -368,7 +368,7 @@
      * to start the repeat, or -1 to disable repeating.
      * </p>
      *
-     * <p>The app should be in foreground for the vibration to happen.</p>
+     * <p>The app should be in the foreground for the vibration to happen.</p>
      *
      * @param pattern an array of longs of times for which to turn the vibrator on or off.
      * @param repeat  the index into pattern at which to repeat, or -1 if
@@ -395,7 +395,7 @@
      * to start the repeat, or -1 to disable repeating.
      * </p>
      *
-     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
      * specify a ringtone, notification or alarm usage in order to vibrate.</p>
      *
      * @param pattern    an array of longs of times for which to turn the vibrator on or off.
@@ -428,7 +428,7 @@
     /**
      * Vibrate with a given effect.
      *
-     * <p>The app should be in foreground for the vibration to happen.</p>
+     * <p>The app should be in the foreground for the vibration to happen.</p>
      *
      * @param vibe {@link VibrationEffect} describing the vibration to be performed.
      */
@@ -440,7 +440,7 @@
     /**
      * Vibrate with a given effect.
      *
-     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
      * specify a ringtone, notification or alarm usage in order to vibrate.</p>
      *
      * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
@@ -461,7 +461,7 @@
     /**
      * Vibrate with a given effect.
      *
-     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * <p>The app should be in the foreground for the vibration to happen. Background apps should
      * specify a ringtone, notification or alarm usage in order to vibrate.</p>
      *
      * @param vibe       {@link VibrationEffect} describing the vibration to be performed.
@@ -477,7 +477,7 @@
 
     /**
      * Like {@link #vibrate(VibrationEffect, VibrationAttributes)}, but allows the
-     * caller to specify the vibration is owned by someone else and set reason for vibration.
+     * caller to specify the vibration is owned by someone else and set a reason for vibration.
      *
      * @hide
      */
@@ -519,7 +519,7 @@
     }
 
     /**
-     * Query whether the vibrator supports all of the given effects.
+     * Query whether the vibrator supports all the given effects.
      *
      * <p>If an effect is not supported, the system may still automatically fall back to a simpler
      * vibration instead, which is not optimised for the specific device, however vibration isn't
@@ -533,7 +533,7 @@
      * vibration.
      *
      * <p>If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know
-     * whether all of the effects are supported. It may support any or all of the queried effects,
+     * whether all the effects are supported. It may support any or all of the queried effects,
      * but there's no way to programmatically know whether a {@link #vibrate} call will successfully
      * cause a vibration. It's guaranteed, however, that none of the queried effects are
      * definitively unsupported by the hardware.
@@ -541,7 +541,7 @@
      * <p>Use {@link #areEffectsSupported(int...)} to get individual results for each effect.
      *
      * @param effectIds Which effects to query for.
-     * @return Whether all of the effects are natively supported by the device.
+     * @return Whether all the effects are natively supported by the device.
      */
     @VibrationEffectSupport
     public final int areAllEffectsSupported(
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 00ce14f..71ec096 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -35,7 +35,8 @@
 /**
  * A VibratorInfo describes the capabilities of a {@link Vibrator}.
  *
- * This description includes its capabilities, list of supported effects and composition primitives.
+ * <p>This description includes its capabilities, list of supported effects and composition
+ * primitives.
  *
  * @hide
  */
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index c82a516..f506ef8 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -25,7 +25,7 @@
 import android.util.Log;
 
 /**
- * Class that provides access to all vibrators from the device, as well as the ability to run them
+ * Provides access to all vibrators from the device, as well as the ability to run them
  * in a synchronized fashion.
  * <p>
  * If your process exits, any vibration you started will stop.
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 02db274..29b4570 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -19,10 +19,54 @@
 /**
  * @hide
  */
-interface ILogcatManagerService {
+oneway interface ILogcatManagerService {
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client makes privileged log data access request.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void startThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client finished the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void finishThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user approved
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void approve(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user declined
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void decline(in int uid, in int gid, in int pid, in int fd);
 }
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4e1337f..cc98339 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1538,6 +1538,8 @@
      * be reserved for cached data depending on the device state which is then passed on
      * to getStorageCacheBytes.
      *
+     * Input File path must point to a storage volume.
+     *
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
index 23b45ae..0f2aa15 100644
--- a/core/java/android/os/vibrator/VibratorFrequencyProfile.java
+++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
@@ -61,8 +61,7 @@
      * <p>The returned list will not be empty, and will have entries representing frequencies from
      * {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive.
      *
-     * @return Array of maximum relative amplitude measurements, each value is between 0 and 1,
-     * inclusive.
+     * @return Array of maximum relative amplitude measurements.
      */
     @NonNull
     @FloatRange(from = 0, to = 1)
diff --git a/core/java/android/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
deleted file mode 100644
index 440d6f2..0000000
--- a/core/java/android/permission/PermGroupUsage.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-
-/**
- * Represents the usage of a permission group by an app. Supports package name, user, permission
- * group, whether or not the access is running or recent, whether the access is tied to a phone
- * call, and an optional special attribution.
- *
- * @hide
- */
-@TestApi
-public final class PermGroupUsage {
-
-    private final String mPackageName;
-    private final int mUid;
-    private final long mLastAccess;
-    private final String mPermGroupName;
-    private final boolean mIsActive;
-    private final boolean mIsPhoneCall;
-    private final CharSequence mAttribution;
-
-    /**
-     *
-     * @param packageName The package name of the using app
-     * @param uid The uid of the using app
-     * @param permGroupName The name of the permission group being used
-     * @param lastAccess The time of last access
-     * @param isActive Whether this is active
-     * @param isPhoneCall Whether this is a usage by the phone
-     * @param attribution An optional string attribution to show
-     * @hide
-     */
-    @TestApi
-    public PermGroupUsage(@NonNull String packageName, int uid,
-            @NonNull String permGroupName, long lastAccess, boolean isActive, boolean isPhoneCall,
-            @Nullable CharSequence attribution) {
-        this.mPackageName = packageName;
-        this.mUid = uid;
-        this.mPermGroupName = permGroupName;
-        this.mLastAccess = lastAccess;
-        this.mIsActive = isActive;
-        this.mIsPhoneCall = isPhoneCall;
-        this.mAttribution = attribution;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @NonNull String getPackageName() {
-        return mPackageName;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public int getUid() {
-        return mUid;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @NonNull String getPermGroupName() {
-        return mPermGroupName;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public long getLastAccess() {
-        return mLastAccess;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public boolean isActive() {
-        return mIsActive;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public boolean isPhoneCall() {
-        return mIsPhoneCall;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @Nullable CharSequence getAttribution() {
-        return mAttribution;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
-                + " packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
-                + mPermGroupName + ", lastAccess: " + mLastAccess + ", isActive: " + mIsActive
-                + ", attribution: " + mAttribution;
-    }
-}
diff --git a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl b/core/java/android/permission/PermissionGroupUsage.aidl
similarity index 81%
rename from apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
rename to core/java/android/permission/PermissionGroupUsage.aidl
index 92d673f..f18698a 100644
--- a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
+++ b/core/java/android/permission/PermissionGroupUsage.aidl
@@ -1,5 +1,5 @@
-/*
- * Copyright 2020, 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media;
+package android.permission;
 
-parcelable MediaParceledListSlice<T>;
+parcelable PermissionGroupUsage;
\ No newline at end of file
diff --git a/core/java/android/permission/PermissionGroupUsage.java b/core/java/android/permission/PermissionGroupUsage.java
new file mode 100644
index 0000000..49b7463
--- /dev/null
+++ b/core/java/android/permission/PermissionGroupUsage.java
@@ -0,0 +1,322 @@
+/*
+ * 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.permission;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents the usage of a permission group by an app. Supports package name, user, permission
+ * group, whether or not the access is running or recent, whether the access is tied to a phone
+ * call, and an optional special attribution tag, label and proxy label.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+        genHiddenConstructor = true,
+        genEqualsHashCode = true,
+        genToString = true
+)
+public final class PermissionGroupUsage implements Parcelable {
+
+    private final @NonNull String mPackageName;
+    private final int mUid;
+    private final long mLastAccessTimeMillis;
+    private final @NonNull String mPermissionGroupName;
+    private final boolean mActive;
+    private final boolean mPhoneCall;
+    private final @Nullable CharSequence mAttributionTag;
+    private final @Nullable CharSequence mAttributionLabel;
+    private final @Nullable CharSequence mProxyLabel;
+
+
+
+    // 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/permission/PermissionGroupUsage.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new PermissionGroupUsage.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public PermissionGroupUsage(
+            @NonNull String packageName,
+            int uid,
+            long lastAccessTimeMillis,
+            @NonNull String permissionGroupName,
+            boolean active,
+            boolean phoneCall,
+            @Nullable CharSequence attributionTag,
+            @Nullable CharSequence attributionLabel,
+            @Nullable CharSequence proxyLabel) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUid = uid;
+        this.mLastAccessTimeMillis = lastAccessTimeMillis;
+        this.mPermissionGroupName = permissionGroupName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPermissionGroupName);
+        this.mActive = active;
+        this.mPhoneCall = phoneCall;
+        this.mAttributionTag = attributionTag;
+        this.mAttributionLabel = attributionLabel;
+        this.mProxyLabel = proxyLabel;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * @return Package name for the usage
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return UID for the usage
+     */
+    @DataClass.Generated.Member
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * @return Last access time in millis for the usage
+     */
+    @CurrentTimeMillisLong
+    @DataClass.Generated.Member
+    public long getLastAccessTimeMillis() {
+        return mLastAccessTimeMillis;
+    }
+
+    /**
+     * @return Permission group name for the usage
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPermissionGroupName() {
+        return mPermissionGroupName;
+    }
+
+    /**
+     * @return If usage is active
+     */
+    @DataClass.Generated.Member
+    public boolean isActive() {
+        return mActive;
+    }
+
+    /**
+     * @return If usage is a phone call
+     */
+    @DataClass.Generated.Member
+    public boolean isPhoneCall() {
+        return mPhoneCall;
+    }
+
+    /**
+     * @return Attribution tag associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getAttributionTag() {
+        return mAttributionTag;
+    }
+
+    /**
+     * @return Attribution label associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getAttributionLabel() {
+        return mAttributionLabel;
+    }
+
+    /**
+     * @return Proxy label associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getProxyLabel() {
+        return mProxyLabel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "PermissionGroupUsage { " +
+                "packageName = " + mPackageName + ", " +
+                "uid = " + mUid + ", " +
+                "lastAccessTimeMillis = " + mLastAccessTimeMillis + ", " +
+                "permissionGroupName = " + mPermissionGroupName + ", " +
+                "active = " + mActive + ", " +
+                "phoneCall = " + mPhoneCall + ", " +
+                "attributionTag = " + mAttributionTag + ", " +
+                "attributionLabel = " + mAttributionLabel + ", " +
+                "proxyLabel = " + mProxyLabel +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(PermissionGroupUsage other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        PermissionGroupUsage that = (PermissionGroupUsage) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mPackageName, that.mPackageName)
+                && mUid == that.mUid
+                && mLastAccessTimeMillis == that.mLastAccessTimeMillis
+                && java.util.Objects.equals(mPermissionGroupName, that.mPermissionGroupName)
+                && mActive == that.mActive
+                && mPhoneCall == that.mPhoneCall
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+                && java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel)
+                && java.util.Objects.equals(mProxyLabel, that.mProxyLabel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + mUid;
+        _hash = 31 * _hash + Long.hashCode(mLastAccessTimeMillis);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPermissionGroupName);
+        _hash = 31 * _hash + Boolean.hashCode(mActive);
+        _hash = 31 * _hash + Boolean.hashCode(mPhoneCall);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel);
+        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) { ... }
+
+        int flg = 0;
+        if (mActive) flg |= 0x10;
+        if (mPhoneCall) flg |= 0x20;
+        if (mAttributionTag != null) flg |= 0x40;
+        if (mAttributionLabel != null) flg |= 0x80;
+        if (mProxyLabel != null) flg |= 0x100;
+        dest.writeInt(flg);
+        dest.writeString(mPackageName);
+        dest.writeInt(mUid);
+        dest.writeLong(mLastAccessTimeMillis);
+        dest.writeString(mPermissionGroupName);
+        if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag);
+        if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel);
+        if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ PermissionGroupUsage(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int flg = in.readInt();
+        boolean active = (flg & 0x10) != 0;
+        boolean phoneCall = (flg & 0x20) != 0;
+        String packageName = in.readString();
+        int uid = in.readInt();
+        long lastAccessTimeMillis = in.readLong();
+        String permissionGroupName = in.readString();
+        CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence();
+        CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence();
+        CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence();
+
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUid = uid;
+        this.mLastAccessTimeMillis = lastAccessTimeMillis;
+        this.mPermissionGroupName = permissionGroupName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPermissionGroupName);
+        this.mActive = active;
+        this.mPhoneCall = phoneCall;
+        this.mAttributionTag = attributionTag;
+        this.mAttributionLabel = attributionLabel;
+        this.mProxyLabel = proxyLabel;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<PermissionGroupUsage> CREATOR
+            = new Parcelable.Creator<PermissionGroupUsage>() {
+        @Override
+        public PermissionGroupUsage[] newArray(int size) {
+            return new PermissionGroupUsage[size];
+        }
+
+        @Override
+        public PermissionGroupUsage createFromParcel(@NonNull android.os.Parcel in) {
+            return new PermissionGroupUsage(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1645067417023L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  int mUid\nprivate final  long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final  boolean mActive\nprivate final  boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fc7ac11..c509de6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -193,6 +193,17 @@
      */
     public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
 
+    /**
+     * Intent extra: List of PermissionGroupUsages
+     * <p>
+     * Type: {@code List<PermissionGroupUsage>}
+     * </p>
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_USAGES =
+            "android.permission.extra.PERMISSION_USAGES";
+
     private final @NonNull Context mContext;
 
     private final IPackageManager mPackageManager;
@@ -1108,7 +1119,7 @@
     @TestApi
     @NonNull
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
-    public List<PermGroupUsage> getIndicatorAppOpUsageData() {
+    public List<PermissionGroupUsage> getIndicatorAppOpUsageData() {
         return getIndicatorAppOpUsageData(new AudioManager().isMicrophoneMute());
     }
 
@@ -1122,7 +1133,7 @@
     @TestApi
     @NonNull
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
-    public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
+    public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
         // Lazily initialize the usage helper
         initializeUsageHelper();
         return mUsageHelper.getOpUsageData(micMuted);
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 658e033..0b57842 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -42,7 +42,10 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.icu.text.ListFormatter;
 import android.media.AudioManager;
 import android.os.Process;
@@ -70,20 +73,30 @@
 public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
         AppOpsManager.OnOpStartedListener {
 
-    /** Whether to show the mic and camera icons.  */
+    /**
+     * Whether to show the mic and camera icons.
+     */
     private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
 
-    /** Whether to show the location indicators. */
+    /**
+     * Whether to show the location indicators.
+     */
     private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
             "location_indicators_enabled";
 
-    /** Whether to show the Permissions Hub.  */
+    /**
+     * Whether to show the Permissions Hub.
+     */
     private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled";
 
-    /** How long after an access to show it as "recent" */
+    /**
+     * How long after an access to show it as "recent"
+     */
     private static final String RECENT_ACCESS_TIME_MS = "recent_access_time_ms";
 
-    /** How long after an access to show it as "running" */
+    /**
+     * How long after an access to show it as "running"
+     */
     private static final String RUNNING_ACCESS_TIME_MS = "running_access_time_ms";
 
     private static final String SYSTEM_PKG = "android";
@@ -132,7 +145,7 @@
     );
 
     private static @NonNull String getGroupForOp(String op) {
-        switch(op) {
+        switch (op) {
             case OPSTR_RECORD_AUDIO:
                 return MICROPHONE;
             case OPSTR_CAMERA:
@@ -158,6 +171,7 @@
 
     /**
      * Constructor for PermissionUsageHelper
+     *
      * @param context The context from which to derive the package information
      */
     public PermissionUsageHelper(@NonNull Context context) {
@@ -167,9 +181,9 @@
         mUserContexts = new ArrayMap<>();
         mUserContexts.put(Process.myUserHandle(), mContext);
         // TODO ntmyren: make this listen for flag enable/disable changes
-        String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+        String[] opStrs = {OPSTR_CAMERA, OPSTR_RECORD_AUDIO};
         mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this);
-        int[] ops = { OP_CAMERA, OP_RECORD_AUDIO };
+        int[] ops = {OP_CAMERA, OP_RECORD_AUDIO};
         mAppOpsManager.startWatchingStarted(ops, this);
     }
 
@@ -225,8 +239,8 @@
 
     @Override
     public void onOpStarted(int op, int uid, String packageName, String attributionTag,
-                @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
-       // not part of an attribution chain. Do nothing
+            @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+        // not part of an attribution chain. Do nothing
     }
 
     @Override
@@ -274,8 +288,8 @@
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
-    public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
-        List<PermGroupUsage> usages = new ArrayList<>();
+    public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) {
+        List<PermissionGroupUsage> usages = new ArrayList<>();
 
         if (!shouldShowIndicators()) {
             return usages;
@@ -313,6 +327,9 @@
             }
         }
 
+        // map of package name -> map of attribution tag -> attribution labels
+        ArrayMap<String, Map<String, String>> subAttributionLabelsMap = new ArrayMap<>();
+
         for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
             boolean isPhone = false;
             String permGroup = usedPermGroups.get(permGroupNum);
@@ -320,6 +337,8 @@
             ArrayMap<OpUsage, CharSequence> usagesWithLabels =
                     getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
 
+            updateSubattributionLabelsMap(rawUsages.get(permGroup), subAttributionLabelsMap);
+
             if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
                 isPhone = true;
                 permGroup = MICROPHONE;
@@ -330,15 +349,86 @@
 
             for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) {
                 OpUsage usage = usagesWithLabels.keyAt(usageNum);
-                usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
-                        usage.lastAccessTime, usage.isRunning, isPhone,
-                        usagesWithLabels.valueAt(usageNum)));
+                String attributionLabel = subAttributionLabelsMap.getOrDefault(usage.packageName,
+                        new ArrayMap<>()).getOrDefault(usage.attributionTag, null);
+                usages.add(
+                        new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
+                                permGroup,
+                                usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
+                                usagesWithLabels.valueAt(usageNum)));
             }
         }
 
         return usages;
     }
 
+    private void updateSubattributionLabelsMap(List<OpUsage> usages,
+            ArrayMap<String, Map<String, String>> subAttributionLabelsMap) {
+        if (usages == null || usages.isEmpty()) {
+            return;
+        }
+        for (OpUsage usage : usages) {
+            if (usage.attributionTag != null && !subAttributionLabelsMap.containsKey(
+                    usage.packageName)) {
+                subAttributionLabelsMap.put(usage.packageName,
+                        getSubattributionLabelsForPackage(usage.packageName, usage.uid));
+            }
+        }
+    }
+
+    /**
+     * Query attribution labels for a package
+     *
+     * @param packageName
+     * @param uid
+     * @return map of attribution tag -> attribution labels for a package
+     */
+    private ArrayMap<String, String> getSubattributionLabelsForPackage(String packageName,
+            int uid) {
+        ArrayMap<String, String> attributionLabelMap = new ArrayMap<>();
+        UserHandle user = UserHandle.getUserHandleForUid(uid);
+        try {
+            if (!isSubattributionSupported(packageName, uid)) {
+                return attributionLabelMap;
+            }
+            Context userContext = getUserContext(user);
+            PackageInfo packageInfo = userContext.getPackageManager().getPackageInfo(
+                    packageName,
+                    PackageManager.GET_PERMISSIONS | PackageManager.GET_ATTRIBUTIONS);
+            Context pkgContext = userContext.createPackageContext(packageInfo.packageName, 0);
+            for (Attribution attribution : packageInfo.attributions) {
+                try {
+                    String resourceForLabel = pkgContext.getString(attribution.getLabel());
+                    attributionLabelMap.put(attribution.getTag(), resourceForLabel);
+                } catch (Resources.NotFoundException e) {
+                    // Shouldn't happen, do nothing
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Did not find the package, do nothing
+        }
+        return attributionLabelMap;
+    }
+
+    /**
+     * Returns true if the app supports subattribution.
+     */
+    private boolean isSubattributionSupported(String packageName, int uid) {
+        try {
+            PackageManager userPkgManager =
+                    getUserContext(UserHandle.getUserHandleForUid(uid)).getPackageManager();
+            ApplicationInfo appInfo = userPkgManager.getApplicationInfoAsUser(packageName,
+                    PackageManager.ApplicationInfoFlags.of(0),
+                    UserHandle.getUserId(uid));
+            if (appInfo != null) {
+                return appInfo.areAttributionsUserVisible();
+            }
+            return false;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Get the raw usages from the system, and then parse out the ones that are not recent enough,
      * determine which permission group each belongs in, and removes duplicates (if the same app
@@ -346,7 +436,6 @@
      * running/recent info, if the usage is a phone call, per permission group.
      *
      * @param opNames a list of op names to get usage for
-     *
      * @return A map of permission group -> list of usages that are recent or running
      */
     private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
@@ -378,6 +467,7 @@
                 List<String> attributionTags =
                         new ArrayList<>(opEntry.getAttributedOpEntries().keySet());
 
+
                 int numAttrEntries = opEntry.getAttributedOpEntries().size();
                 for (int attrOpEntryNum = 0; attrOpEntryNum < numAttrEntries; attrOpEntryNum++) {
                     String attributionTag = attributionTags.get(attrOpEntryNum);
@@ -456,6 +546,7 @@
         ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
         // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
         ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
+
         for (int i = 0; i < usages.size(); i++) {
             OpUsage usage = usages.get(i);
             allUsages.put(usage.getPackageIdHash(), usage);
@@ -588,7 +679,6 @@
                     } catch (PackageManager.NameNotFoundException e) {
                         // do nothing
                     }
-
                 }
                 usagesAndLabels.put(start.usage, proxyLabel);
             }
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
index f5c59b5..462ac14 100644
--- a/core/java/android/service/attention/AttentionService.java
+++ b/core/java/android/service/attention/AttentionService.java
@@ -128,9 +128,9 @@
 
         /** {@inheritDoc} */
         @Override
-        public void onStartProximityUpdates(IProximityCallback callback) {
+        public void onStartProximityUpdates(IProximityUpdateCallback callback) {
             Objects.requireNonNull(callback);
-            AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback));
+            AttentionService.this.onStartProximityUpdates(new ProximityUpdateCallback(callback));
 
         }
 
@@ -166,11 +166,11 @@
 
     /**
      * Requests the continuous updates of proximity signal via the provided callback,
-     * until the given callback is unregistered.
+     * until {@link #onStopProximityUpdates} is called.
      *
      * @param callback the callback to return the result to
      */
-    public void onStartProximityUpdates(@NonNull ProximityCallback callback) {
+    public void onStartProximityUpdates(@NonNull ProximityUpdateCallback callback) {
         Slog.w(LOG_TAG, "Override this method.");
     }
 
@@ -213,22 +213,24 @@
         }
     }
 
-    /** Callbacks for ProximityCallback results. */
-    public static final class ProximityCallback {
-        @NonNull private final WeakReference<IProximityCallback> mCallback;
+    /** Callbacks for ProximityUpdateCallback results. */
+    public static final class ProximityUpdateCallback {
+        @NonNull private final WeakReference<IProximityUpdateCallback> mCallback;
 
-        private ProximityCallback(@NonNull IProximityCallback callback) {
+        private ProximityUpdateCallback(@NonNull IProximityUpdateCallback callback) {
             mCallback = new WeakReference<>(callback);
         }
 
         /**
          * @param distance the estimated distance of the user (in meter)
-         * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
-         *
+         * The distance will be {@link #PROXIMITY_UNKNOWN} if the proximity sensing
+         * was inconclusive.
          */
         public void onProximityUpdate(double distance) {
             try {
-                mCallback.get().onProximityUpdate(distance);
+                if (mCallback.get() != null) {
+                    mCallback.get().onProximityUpdate(distance);
+                }
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl
index 8bb881b..e3ce114 100644
--- a/core/java/android/service/attention/IAttentionService.aidl
+++ b/core/java/android/service/attention/IAttentionService.aidl
@@ -17,7 +17,7 @@
 package android.service.attention;
 
 import android.service.attention.IAttentionCallback;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 
 /**
  * Interface for a concrete implementation to provide to the AttentionManagerService.
@@ -27,6 +27,6 @@
 oneway interface IAttentionService {
     void checkAttention(IAttentionCallback callback);
     void cancelAttentionCheck(IAttentionCallback callback);
-    void onStartProximityUpdates(IProximityCallback callback);
+    void onStartProximityUpdates(IProximityUpdateCallback callback);
     void onStopProximityUpdates();
 }
\ No newline at end of file
diff --git a/core/java/android/service/attention/IProximityCallback.aidl b/core/java/android/service/attention/IProximityUpdateCallback.aidl
similarity index 77%
rename from core/java/android/service/attention/IProximityCallback.aidl
rename to core/java/android/service/attention/IProximityUpdateCallback.aidl
index 9ecf9bc..26daa5c 100644
--- a/core/java/android/service/attention/IProximityCallback.aidl
+++ b/core/java/android/service/attention/IProximityUpdateCallback.aidl
@@ -5,6 +5,6 @@
  *
  * @hide
  */
-oneway interface IProximityCallback {
+oneway interface IProximityUpdateCallback {
     void onProximityUpdate(double distance);
 }
diff --git a/core/java/android/service/autofill/OWNERS b/core/java/android/service/autofill/OWNERS
index a088632..9a30e82 100644
--- a/core/java/android/service/autofill/OWNERS
+++ b/core/java/android/service/autofill/OWNERS
@@ -1,9 +1,3 @@
 # Bug component: 351486
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/java/android/service/contentcapture/OWNERS b/core/java/android/service/contentcapture/OWNERS
index 6337327..24561c5 100644
--- a/core/java/android/service/contentcapture/OWNERS
+++ b/core/java/android/service/contentcapture/OWNERS
@@ -1,9 +1,3 @@
 # Bug component: 544200
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+include /core/java/android/view/contentcapture/OWNERS
diff --git a/core/java/android/service/contentsuggestions/OWNERS b/core/java/android/service/contentsuggestions/OWNERS
index 46b5ea0..72fe0b1 100644
--- a/core/java/android/service/contentsuggestions/OWNERS
+++ b/core/java/android/service/contentsuggestions/OWNERS
@@ -1,7 +1,2 @@
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+
+include /core/java/android/app/contentsuggestions/OWNERS
diff --git a/core/java/android/service/resumeonreboot/OWNERS b/core/java/android/service/resumeonreboot/OWNERS
index 3a127d5..721fbaf 100644
--- a/core/java/android/service/resumeonreboot/OWNERS
+++ b/core/java/android/service/resumeonreboot/OWNERS
@@ -1,2 +1 @@
-xunchang@google.com
-zhaojiac@google.com
+ejyzhang@google.com
\ No newline at end of file
diff --git a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
index 08a7205..f028ed3 100644
--- a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -24,6 +24,8 @@
 import android.util.SparseArray;
 import android.view.selectiontoolbar.ShowInfo;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.UUID;
 
 /**
@@ -130,5 +132,22 @@
             }
         }
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        int size = mToolbarCache.size();
+        pw.print("number selectionToolbar: "); pw.println(size);
+        String pfx = "  ";
+        for (int i = 0; i < size; i++) {
+            pw.print("#"); pw.println(i);
+            int callingUid = mToolbarCache.keyAt(i);
+            pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid);
+            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
+            RemoteSelectionToolbar selectionToolbar = toolbarPair.second;
+            pw.print(pfx); pw.print("selectionToolbar: ");
+            selectionToolbar.dump(pfx, pw);
+            pw.println();
+        }
+    }
 }
 
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
index 04491f0..8fe6f71 100644
--- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -24,6 +24,8 @@
 import android.view.MotionEvent;
 import android.widget.LinearLayout;
 
+import java.io.PrintWriter;
+
 /**
  * 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.
@@ -40,6 +42,9 @@
     private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
     private Rect mContentRect;
 
+    private int mLastDownX = -1;
+    private int mLastDownY = -1;
+
     public FloatingToolbarRoot(Context context, IBinder targetInputToken,
             SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
         super(context);
@@ -59,13 +64,13 @@
     @SuppressLint("ClickableViewAccessibility")
     public boolean dispatchTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            int downX = (int) event.getX();
-            int downY = (int) event.getY();
+            mLastDownX = (int) event.getX();
+            mLastDownY = (int) event.getY();
             if (DEBUG) {
-                Log.d(TAG, "downX=" + downX + " downY=" + downY);
+                Log.d(TAG, "downX=" + mLastDownX + " downY=" + mLastDownY);
             }
             // TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED
-            if (!mContentRect.contains(downX, downY)) {
+            if (!mContentRect.contains(mLastDownX, mLastDownY)) {
                 if (DEBUG) {
                     Log.d(TAG, "Transfer touch focus to application.");
                 }
@@ -75,4 +80,10 @@
         }
         return super.dispatchTouchEvent(event);
     }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("FloatingToolbarRoot:");
+        pw.print(prefix + "  "); pw.print("last down X: "); pw.println(mLastDownX);
+        pw.print(prefix + "  "); pw.print("last down Y: "); pw.println(mLastDownY);
+    }
 }
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 179d39d..d75fbc0 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -58,6 +58,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
+import java.io.PrintWriter;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
@@ -1318,6 +1319,7 @@
         contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
         contentContainer.setClipToOutline(true);
         return contentContainer;
     }
@@ -1371,4 +1373,32 @@
             Log.v(TAG, message);
         }
     }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("toolbar token: "); pw.println(mSelectionToolbarToken);
+        pw.print(prefix); pw.print("dismissed: "); pw.println(mDismissed);
+        pw.print(prefix); pw.print("hidden: "); pw.println(mHidden);
+        pw.print(prefix); pw.print("popup width: "); pw.println(mPopupWidth);
+        pw.print(prefix); pw.print("popup height: "); pw.println(mPopupHeight);
+        pw.print(prefix); pw.print("relative coords: "); pw.println(mRelativeCoordsForToolbar);
+        pw.print(prefix); pw.print("main panel size: "); pw.println(mMainPanelSize);
+        boolean hasOverflow = hasOverflow();
+        pw.print(prefix); pw.print("has overflow: "); pw.println(hasOverflow);
+        if (hasOverflow) {
+            pw.print(prefix); pw.print("overflow open: "); pw.println(mIsOverflowOpen);
+            pw.print(prefix); pw.print("overflow size: "); pw.println(mOverflowPanelSize);
+        }
+        if (mSurfaceControlViewHost != null) {
+            FloatingToolbarRoot root = (FloatingToolbarRoot) mSurfaceControlViewHost.getView();
+            root.dump(prefix, pw);
+        }
+        if (mMenuItems != null) {
+            int menuItemSize = mMenuItems.size();
+            pw.print(prefix); pw.print("number menu items: "); pw.println(menuItemSize);
+            for (int i = 0; i < menuItemSize; i++) {
+                pw.print(prefix); pw.print("#"); pw.println(i);
+                pw.print(prefix + "  "); pw.println(mMenuItems.get(i));
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/textclassifier/OWNERS b/core/java/android/service/textclassifier/OWNERS
index a535f52..c85c69e 100644
--- a/core/java/android/service/textclassifier/OWNERS
+++ b/core/java/android/service/textclassifier/OWNERS
@@ -1,9 +1,3 @@
 # Bug component: 709498
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+include /core/java/android/view/textclassifier/OWNERS
diff --git a/core/java/android/service/tracing/TraceReportService.java b/core/java/android/service/tracing/TraceReportService.java
index 3d16a3d..6fdc8e8 100644
--- a/core/java/android/service/tracing/TraceReportService.java
+++ b/core/java/android/service/tracing/TraceReportService.java
@@ -112,7 +112,6 @@
         }
     }
 
-    // Methods to override.
     /**
      * Called when a trace is reported and sent to this class.
      *
@@ -123,15 +122,10 @@
     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) {
+    private 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.");
@@ -153,10 +147,12 @@
 
     /**
      * Returns an IBinder for handling binder calls from system_server.
+     *
+     * @hide
      */
     @Nullable
     @Override
-    public IBinder onBind(@NonNull Intent intent) {
+    public final IBinder onBind(@NonNull Intent intent) {
         if (mMessenger == null) {
             mMessenger = new Messenger(new Handler(Looper.getMainLooper(), this::onMessage));
         }
diff --git a/core/java/android/service/translation/OWNERS b/core/java/android/service/translation/OWNERS
index a1e663a..440f9a8 100644
--- a/core/java/android/service/translation/OWNERS
+++ b/core/java/android/service/translation/OWNERS
@@ -1,8 +1,3 @@
 # Bug component: 994311
 
-adamhe@google.com
-augale@google.com
-joannechung@google.com
-lpeter@google.com
-svetoslavganov@google.com
-tymtsai@google.com
+include /core/java/android/view/translation/OWNERS
diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS
index 46b5ea0..59a0c2e 100644
--- a/core/java/android/service/voice/OWNERS
+++ b/core/java/android/service/voice/OWNERS
@@ -1,7 +1,3 @@
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
-augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+# Bug component: 533220
+
+include /core/java/android/app/assist/OWNERS
diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl
index 986a41c..f7e459e 100644
--- a/core/java/android/speech/IRecognitionListener.aidl
+++ b/core/java/android/speech/IRecognitionListener.aidl
@@ -79,7 +79,7 @@
 
     /**
      * Called for each ready segment of a recognition request. To request segmented speech results
-     * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called
+     * use {@link RecognizerIntent#EXTRA_SEGMENTED_SESSION}. The callback might be called
      * any number of times between {@link #onBeginningOfSpeech()} and
      * {@link #onEndOfSegmentedSession()}.
      *
@@ -91,7 +91,7 @@
 
     /**
      * Called at the end of a segmented recognition request. To request segmented speech results
-     * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+     * use {@link RecognizerIntent#EXTRA_SEGMENTED_SESSION}.
      */
     void onEndOfSegmentedSession();
 
diff --git a/core/java/android/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java
index 64fd09f..ee4a7d0 100644
--- a/core/java/android/speech/RecognitionListener.java
+++ b/core/java/android/speech/RecognitionListener.java
@@ -74,7 +74,7 @@
      * <p>
      *     Called with the results for the full speech since {@link #onReadyForSpeech(Bundle)}.
      *     To get recognition results in segments rather than for the full session see
-     *     {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+     *     {@link RecognizerIntent#EXTRA_SEGMENTED_SESSION}.
      * </p>
      *
      * @param results the recognition results. To retrieve the results in {@code
@@ -100,7 +100,7 @@
 
     /**
      * Called for each ready segment of a recognition request. To request segmented speech results
-     * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called
+     * use {@link RecognizerIntent#EXTRA_SEGMENTED_SESSION}. The callback might be called
      * any number of times between {@link #onReadyForSpeech(Bundle)} and
      * {@link #onEndOfSegmentedSession()}.
      *
@@ -112,7 +112,7 @@
 
     /**
      * Called at the end of a segmented recognition request. To request segmented speech results
-     * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+     * use {@link RecognizerIntent#EXTRA_SEGMENTED_SESSION}.
      */
     default void onEndOfSegmentedSession() {}
 
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 22cffc1..6a65efb 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -199,7 +199,7 @@
     }
 
     private void dispatchTriggerModelDownload(Intent intent) {
-        RecognitionService.this.triggerModelDownload(intent);
+        RecognitionService.this.onTriggerModelDownload(intent);
     }
 
     private class StartListeningArgs {
@@ -283,7 +283,7 @@
     /**
      * Requests the download of the recognizer support for {@code recognizerIntent}.
      */
-    public void triggerModelDownload(@NonNull Intent recognizerIntent) {
+    public void onTriggerModelDownload(@NonNull Intent recognizerIntent) {
         if (DBG) {
             Log.i(TAG, String.format("#downloadModel [%s]", recognizerIntent));
         }
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 271e307..cd18c19 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -32,21 +32,6 @@
  * Constants for supporting speech recognition through starting an {@link Intent}
  */
 public class RecognizerIntent {
-    /**
-     * The extra key used in an intent to the speech recognizer for voice search. Not
-     * generally to be used by developers. The system search dialog uses this, for example,
-     * to set a calling package for identification by a voice search API. If this extra
-     * is set by anyone but the system process, it should be overridden by the voice search
-     * implementation.
-     */
-    public static final String EXTRA_CALLING_PACKAGE = "calling_package";
-
-    /**
-     * The extra key used in an intent which is providing an already opened audio source for the
-     * RecognitionService to use. Data should be a URI to an audio resource.
-     */
-    public static final String EXTRA_AUDIO_INJECT_SOURCE =
-            "android.speech.extra.AUDIO_INJECT_SOURCE";
 
     private RecognizerIntent() {
         // Not for instantiating.
@@ -58,7 +43,7 @@
      * {@link Activity#onActivityResult}, if you start the intent using
      * {@link Activity#startActivityForResult(Intent, int)}), or forwarded via a PendingIntent
      * if one is provided.
-     * 
+     *
      * <p>Starting this intent with just {@link Activity#startActivity(Intent)} is not supported.
      * You must either use {@link Activity#startActivityForResult(Intent, int)}, or provide a
      * PendingIntent, to receive recognition results.
@@ -70,7 +55,7 @@
      * <ul>
      *   <li>{@link #EXTRA_LANGUAGE_MODEL}
      * </ul>
-     * 
+     *
      * <p>Optional extras:
      * <ul>
      *   <li>{@link #EXTRA_PROMPT}
@@ -79,12 +64,12 @@
      *   <li>{@link #EXTRA_RESULTS_PENDINGINTENT}
      *   <li>{@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE}
      * </ul>
-     * 
+     *
      * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      * </ul>
-     * 
+     *
      * <p>NOTE: There may not be any applications installed to handle this action, so you should
      * make sure to catch {@link ActivityNotFoundException}.
      */
@@ -97,12 +82,12 @@
      *
      * <p>If you want to avoid triggering any type of action besides web search, you can use
      * the {@link #EXTRA_WEB_SEARCH_ONLY} extra.
-     * 
+     *
      * <p>Required extras:
      * <ul>
      *   <li>{@link #EXTRA_LANGUAGE_MODEL}
      * </ul>
-     * 
+     *
      * <p>Optional extras:
      * <ul>
      *   <li>{@link #EXTRA_PROMPT}
@@ -112,13 +97,13 @@
      *   <li>{@link #EXTRA_WEB_SEARCH_ONLY}
      *   <li>{@link #EXTRA_ORIGIN}
      * </ul>
-     * 
+     *
      * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      *   <li>{@link #EXTRA_CONFIDENCE_SCORES} (optional)
      * </ul>
-     * 
+     *
      * <p>NOTE: There may not be any applications installed to handle this action, so you should
      * make sure to catch {@link ActivityNotFoundException}.
      */
@@ -157,6 +142,129 @@
             "android.speech.action.VOICE_SEARCH_HANDS_FREE";
 
     /**
+     * Optional {@link android.os.ParcelFileDescriptor} pointing to an already opened audio
+     * source for the recognizer to use. The caller of the recognizer is responsible for closing
+     * the audio. If this extra is not set or the recognizer does not support this feature, the
+     * recognizer will open the mic for audio and close it when the recognition is finished.
+     *
+     * <p>Along with this extra, please send {@link #EXTRA_AUDIO_SOURCE_CHANNEL_COUNT},
+     * {@link #EXTRA_AUDIO_SOURCE_ENCODING}, and {@link #EXTRA_AUDIO_SOURCE_SAMPLING_RATE}
+     * extras, otherwise the default values of these extras will be used.
+     *
+     * <p>Additionally, {@link #EXTRA_ENABLE_BIASING_DEVICE_CONTEXT} may have no effect when this
+     * extra is set.
+     *
+     * <p>This can also be used as the string value for {@link #EXTRA_SEGMENTED_SESSION} to
+     * enable segmented session mode. The audio must be passed in using this extra. The
+     * recognition session will end when and only when the audio is closed.
+     *
+     * @see #EXTRA_SEGMENTED_SESSION
+     */
+    public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+
+    /**
+     * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the number of
+     * channels in the audio. The default value is 1.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT =
+            "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+
+    /**
+     * Optional integer (from {@link android.media.AudioFormat}), to be used with
+     * {@link #EXTRA_AUDIO_SOURCE}, to indicate the audio encoding. The default value is
+     * {@link android.media.AudioFormat#ENCODING_PCM_16BIT}.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_ENCODING =
+            "android.speech.extra.AUDIO_SOURCE_ENCODING";
+
+    /**
+     * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the sampling
+     * rate of the audio. The default value is 16000.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE =
+            "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+
+    /**
+     * Optional boolean to enable biasing towards device context. The recognizer will use the
+     * device context to tune the recognition results.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     */
+    public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT =
+            "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+
+    /**
+     * Optional list of strings, towards which the recognizer should bias the recognition results.
+     * These are separate from the device context.
+     */
+    public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
+
+    /**
+     * Optional string to enable text formatting (e.g. unspoken punctuation (examples: question
+     * mark, comma, period, etc.), capitalization, etc.) and specify the optimization strategy.
+     * If set, the partial and final result texts will be formatted. Each result list will
+     * contain two hypotheses in the order of 1) formatted text 2) raw text.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @see #FORMATTING_OPTIMIZE_QUALITY
+     * @see #FORMATTING_OPTIMIZE_LATENCY
+     */
+    public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+
+    /**
+     * Optimizes formatting quality. This will increase latency but provide the highest
+     * punctuation quality. This is a value to use for {@link #EXTRA_ENABLE_FORMATTING}.
+     *
+     * @see #EXTRA_ENABLE_FORMATTING
+     */
+    public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
+    /**
+     * Optimizes formatting latency. This will result in a slightly lower quality of punctuation
+     * but can improve the experience for real-time use cases. This is a value to use for
+     * {@link #EXTRA_ENABLE_FORMATTING}.
+     *
+     * @see #EXTRA_ENABLE_FORMATTING
+     */
+    public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+
+    /**
+     * Optional boolean, to be used with {@link #EXTRA_ENABLE_FORMATTING}, to prevent the
+     * recognizer adding punctuation after the last word of the partial results. The default is
+     * false.
+     */
+    public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION =
+            "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
+
+    /**
+     * Optional boolean indicating whether the recognizer should mask the offensive words in
+     * recognition results. The Default is true.
+     */
+    public static final String EXTRA_MASK_OFFENSIVE_WORDS =
+            "android.speech.extra.MASK_OFFENSIVE_WORDS";
+
+    /**
+     * The extra key used in an intent to the speech recognizer for voice search. Not
+     * generally to be used by developers. The system search dialog uses this, for example,
+     * to set a calling package for identification by a voice search API. If this extra
+     * is set by anyone but the system process, it should be overridden by the voice search
+     * implementation.
+     */
+    public static final String EXTRA_CALLING_PACKAGE = "calling_package";
+
+    /**
+     * The extra key used in an intent which is providing an already opened audio source for the
+     * RecognitionService to use. Data should be a URI to an audio resource.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @deprecated Replaced with {@link #EXTRA_AUDIO_SOURCE}
+     */
+    @Deprecated
+    public static final String EXTRA_AUDIO_INJECT_SOURCE =
+            "android.speech.extra.AUDIO_INJECT_SOURCE";
+
+    /**
      * Optional boolean to indicate that a "hands free" voice search was performed while the device
      * was in a secure mode. An example of secure mode is when the device's screen lock is active,
      * and it requires some form of authentication to be unlocked.
@@ -168,24 +276,29 @@
     public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
 
     /**
-     * The minimum length of an utterance. We will not stop recording before this amount of time.
-     * 
-     * Note that it is extremely rare you'd want to specify this value in an intent. If you don't
-     * have a very good reason to change these, you should leave them as they are. Note also that
-     * certain values may cause undesired or unexpected results - use judiciously! Additionally,
-     * depending on the recognizer implementation, these values may have no effect.
+     * Optional integer to indicate the minimum length of the recognition session. The recognizer
+     * will not stop recognizing speech before this amount of time.
+     *
+     * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+     * Generally, it should be specified only when it is also used as the value for
+     * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+     * values may cause undesired or unexpected results - use judiciously!
+     *
+     * <p>Depending on the recognizer implementation, these values may have no effect.
      */
     public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS =
             "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
 
     /**
-     * The amount of time that it should take after we stop hearing speech to consider the input
-     * complete. 
-     * 
-     * Note that it is extremely rare you'd want to specify this value in an intent. If
-     * you don't have a very good reason to change these, you should leave them as they are. Note
-     * also that certain values may cause undesired or unexpected results - use judiciously!
-     * Additionally, depending on the recognizer implementation, these values may have no effect.
+     * The amount of time that it should take after the recognizer stops hearing speech to
+     * consider the input complete hence end the recognition session.
+     *
+     * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+     * Generally, it should be specified only when it is also used as the value for
+     * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+     * values may cause undesired or unexpected results - use judiciously!
+     *
+     * <p>Depending on the recognizer implementation, these values may have no effect.
      */
     public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS =
             "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
@@ -193,8 +306,8 @@
     /**
      * The amount of time that it should take after we stop hearing speech to consider the input
      * possibly complete. This is used to prevent the endpointer cutting off during very short
-     * mid-speech pauses. 
-     * 
+     * mid-speech pauses.
+     *
      * Note that it is extremely rare you'd want to specify this value in an intent. If
      * you don't have a very good reason to change these, you should leave them as they are. Note
      * also that certain values may cause undesired or unexpected results - use judiciously!
@@ -208,21 +321,21 @@
      * {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this
      * information to fine tune the results. This extra is required. Activities implementing
      * {@link #ACTION_RECOGNIZE_SPEECH} may interpret the values as they see fit.
-     * 
+     *
      *  @see #LANGUAGE_MODEL_FREE_FORM
      *  @see #LANGUAGE_MODEL_WEB_SEARCH
      */
     public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
 
-    /** 
-     * Use a language model based on free-form speech recognition.  This is a value to use for 
-     * {@link #EXTRA_LANGUAGE_MODEL}. 
+    /**
+     * Use a language model based on free-form speech recognition.  This is a value to use for
+     * {@link #EXTRA_LANGUAGE_MODEL}.
      * @see #EXTRA_LANGUAGE_MODEL
      */
     public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
-    /** 
-     * Use a language model based on web search terms.  This is a value to use for 
-     * {@link #EXTRA_LANGUAGE_MODEL}. 
+    /**
+     * Use a language model based on web search terms.  This is a value to use for
+     * {@link #EXTRA_LANGUAGE_MODEL}.
      * @see #EXTRA_LANGUAGE_MODEL
      */
     public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
@@ -236,7 +349,7 @@
      * {@link java.util.Locale#getDefault()}.
      */
     public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
-    
+
     /**
      * Optional value which can be used to indicate the referer url of a page in which
      * speech was requested. For example, a web browser may choose to provide this for
@@ -244,12 +357,12 @@
      */
     public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
 
-    /** 
+    /**
      * Optional limit on the maximum number of results to return. If omitted the recognizer
      * will choose how many results to return. Must be an integer.
      */
     public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
-    
+
     /**
      * Optional boolean, to be used with {@link #ACTION_WEB_SEARCH}, to indicate whether to
      * only fire web searches in response to a user's speech. The default is false, meaning
@@ -267,18 +380,18 @@
     /**
      * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will
      * return results to you via the activity results mechanism.  Alternatively, if you use this
-     * extra to supply a PendingIntent, the results will be added to its bundle and the 
+     * extra to supply a PendingIntent, the results will be added to its bundle and the
      * PendingIntent will be sent to its target.
      */
-    public static final String EXTRA_RESULTS_PENDINGINTENT = 
+    public static final String EXTRA_RESULTS_PENDINGINTENT =
             "android.speech.extra.RESULTS_PENDINGINTENT";
-    
+
     /**
      * If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can
      * also use this extra to supply additional extras for the final intent.  The search results
      * will be added to this bundle, and the combined bundle will be sent to the target.
      */
-    public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = 
+    public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE =
             "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
 
     /** Result code returned when no matches are found for the given speech */
@@ -301,7 +414,7 @@
      * the lack of this extra indicates failure.
      */
     public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
-    
+
     /**
      * A float array of confidence scores of the recognition results when performing
      * {@link #ACTION_RECOGNIZE_SPEECH}. The array should be the same size as the ArrayList
@@ -317,7 +430,7 @@
      * returned in an activity result.
      */
     public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
-    
+
     /**
      * Returns the broadcast intent to fire with
      * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}
@@ -334,7 +447,7 @@
      * (Whether these are actually provided is up to the particular implementation. It is
      * recommended that {@link Activity}s implementing {@link #ACTION_WEB_SEARCH} provide this
      * information, but it is not required.)
-     * 
+     *
      * @param context a context object
      * @return the broadcast intent to fire or null if not available
      */
@@ -343,15 +456,15 @@
         ResolveInfo ri = context.getPackageManager().resolveActivity(
                 voiceSearchIntent, PackageManager.GET_META_DATA);
         if (ri == null || ri.activityInfo == null || ri.activityInfo.metaData == null) return null;
-        
+
         String className = ri.activityInfo.metaData.getString(DETAILS_META_DATA);
         if (className == null) return null;
-        
+
         Intent detailsIntent = new Intent(ACTION_GET_LANGUAGE_DETAILS);
         detailsIntent.setComponent(new ComponentName(ri.activityInfo.packageName, className));
         return detailsIntent;
     }
-    
+
     /**
      * Meta-data name under which an {@link Activity} implementing {@link #ACTION_WEB_SEARCH} can
      * use to expose the class name of a {@link BroadcastReceiver} which can respond to request for
@@ -370,7 +483,7 @@
      * are required to implement this. Thus retrieving this meta-data may be null.
      */
     public static final String DETAILS_META_DATA = "android.speech.DETAILS";
-    
+
     /**
      * A broadcast intent which can be fired to the {@link BroadcastReceiver} component specified
      * in the meta-data defined in the {@link #DETAILS_META_DATA} meta-data of an
@@ -388,7 +501,7 @@
      */
     public static final String ACTION_GET_LANGUAGE_DETAILS =
             "android.speech.action.GET_LANGUAGE_DETAILS";
-    
+
     /**
      * Specify this boolean extra in a broadcast of {@link #ACTION_GET_LANGUAGE_DETAILS} to
      * indicate that only the current language preference is needed in the response. This
@@ -397,7 +510,7 @@
      */
     public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE =
             "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
-    
+
     /**
      * The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
      * which is a {@link String} that represents the current language preference this user has
@@ -405,7 +518,7 @@
      */
     public static final String EXTRA_LANGUAGE_PREFERENCE =
             "android.speech.extra.LANGUAGE_PREFERENCE";
-    
+
     /**
      * The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
      * which is an {@link ArrayList} of {@link String}s that represents the languages supported by
@@ -428,14 +541,21 @@
     public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
 
     /**
-     * Optional boolean, when true and supported by the recognizer implementation it will split
-     * the recognition results in segments, returned via
-     * {@link RecognitionListener#onSegmentResults(Bundle)} and terminate the session with
-     * {@link RecognitionListener#onEndOfSegmentedSession()}. There will be no call to
-     * {@link RecognitionListener#onResults(Bundle)}. Callers can use
-     * {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} and
-     * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS} to tune how long the segments
-     * will be. Defaults to false.
+     * Optional string to enable segmented session mode of the specified type, which can be
+     * {@link #EXTRA_AUDIO_SOURCE}, {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} or
+     * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS}. When segmented session mode is
+     * supported by the recognizer implementation and this extra is set, it will return the
+     * recognition results in segments via {@link RecognitionListener#onSegmentResults(Bundle)}
+     * and terminate the session with {@link RecognitionListener#onEndOfSegmentedSession()}.
+     *
+     * <p>When setting this extra, make sure the extra used as the string value here is also set
+     * in the same intent with proper value.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @see #EXTRA_AUDIO_SOURCE
+     * @see #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
+     * @see #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
      */
-    public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+    public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
 }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index e075c05..832db91 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -17,6 +17,7 @@
 package android.speech;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -38,7 +39,6 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -49,6 +49,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Queue;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 
 /**
@@ -206,10 +207,9 @@
                     handleSetTemporaryComponent((ComponentName) msg.obj);
                     break;
                 case MSG_CHECK_RECOGNITION_SUPPORT:
-                    Pair<Intent, RecognitionSupportCallback> intentAndListener =
-                            (Pair<Intent, RecognitionSupportCallback>) msg.obj;
+                    CheckRecognitionSupportArgs args = (CheckRecognitionSupportArgs) msg.obj;
                     handleCheckRecognitionSupport(
-                            intentAndListener.first, intentAndListener.second);
+                            args.mIntent, args.mCallbackExecutor, args.mCallback);
                     break;
                 case MSG_TRIGGER_MODEL_DOWNLOAD:
                     handleTriggerModelDownload((Intent) msg.obj);
@@ -492,6 +492,7 @@
      */
     public void checkRecognitionSupport(
             @NonNull Intent recognizerIntent,
+            @NonNull @CallbackExecutor Executor executor,
             @NonNull RecognitionSupportCallback supportListener) {
         Objects.requireNonNull(recognizerIntent, "intent must not be null");
         Objects.requireNonNull(supportListener, "listener must not be null");
@@ -508,13 +509,13 @@
             connectToSystemService();
         }
         putMessage(Message.obtain(mHandler, MSG_CHECK_RECOGNITION_SUPPORT,
-                Pair.create(recognizerIntent, supportListener)));
+                new CheckRecognitionSupportArgs(recognizerIntent, executor, supportListener)));
     }
 
     /**
      * Attempts to download the support for the given {@code recognizerIntent}. This might trigger
      * user interaction to approve the download. Callers can verify the status of the request via
-     * {@link #checkRecognitionSupport(Intent, RecognitionSupportCallback)}.
+     * {@link #checkRecognitionSupport(Intent, Executor, RecognitionSupportCallback)}.
      *
      * @param recognizerIntent contains parameters for the recognition to be performed. The intent
      *        may also contain optional extras, see {@link RecognizerIntent}.
@@ -625,18 +626,20 @@
     }
 
     private void handleCheckRecognitionSupport(
-            Intent recognizerIntent, RecognitionSupportCallback recognitionSupportCallback) {
+            Intent recognizerIntent,
+            Executor callbackExecutor,
+            RecognitionSupportCallback recognitionSupportCallback) {
         if (!maybeInitializeManagerService()) {
             return;
         }
         try {
             mService.checkRecognitionSupport(
                     recognizerIntent,
-                    new InternalSupportCallback(recognitionSupportCallback));
+                    new InternalSupportCallback(callbackExecutor, recognitionSupportCallback));
             if (DBG) Log.d(TAG, "service support command succeeded");
         } catch (final RemoteException e) {
             Log.e(TAG, "checkRecognitionSupport() failed", e);
-            mListener.onError(ERROR_CLIENT);
+            callbackExecutor.execute(() -> recognitionSupportCallback.onError(ERROR_CLIENT));
         }
     }
 
@@ -780,6 +783,21 @@
         return ComponentName.unflattenFromString(serviceComponent);
     }
 
+    private static class CheckRecognitionSupportArgs {
+        final Intent mIntent;
+        final Executor mCallbackExecutor;
+        final RecognitionSupportCallback mCallback;
+
+        private CheckRecognitionSupportArgs(
+                Intent intent,
+                Executor callbackExecutor,
+                RecognitionSupportCallback callback) {
+            mIntent = intent;
+            mCallbackExecutor = callbackExecutor;
+            mCallback = callback;
+        }
+    }
+
     /**
      * Internal wrapper of IRecognitionListener which will propagate the results to
      * RecognitionListener
@@ -890,40 +908,22 @@
     }
 
     private static class InternalSupportCallback extends IRecognitionSupportCallback.Stub {
+        private final Executor mExecutor;
         private final RecognitionSupportCallback mCallback;
 
-        private static final int MSG_SUPPORT_RESULT = 1;
-        private static final int MSG_ERROR = 2;
-
-        private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-                if (mCallback == null) {
-                    return;
-                }
-                switch (msg.what) {
-                    case MSG_SUPPORT_RESULT:
-                        mCallback.onSupportResult((RecognitionSupport) msg.obj);
-                        break;
-                    case MSG_ERROR:
-                        mCallback.onError((Integer) msg.obj);
-                        break;
-                }
-            }
-        };
-
-        private InternalSupportCallback(RecognitionSupportCallback callback) {
+        private InternalSupportCallback(Executor executor, RecognitionSupportCallback callback) {
+            this.mExecutor = executor;
             this.mCallback = callback;
         }
 
         @Override
         public void onSupportResult(RecognitionSupport recognitionSupport) throws RemoteException {
-            Message.obtain(mInternalHandler, MSG_SUPPORT_RESULT, recognitionSupport).sendToTarget();
+            mExecutor.execute(() -> mCallback.onSupportResult(recognitionSupport));
         }
 
         @Override
         public void onError(int errorCode) throws RemoteException {
-            Message.obtain(mInternalHandler, MSG_ERROR, errorCode).sendToTarget();
+            mExecutor.execute(() -> mCallback.onError(errorCode));
         }
     }
 }
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 748d551..bedd409 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -287,6 +287,16 @@
     }
 
     /**
+     * Retrieves the font metrics for the given range.
+     *
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+     */
+    public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Paint.FontMetricsInt fmi) {
+        mMeasuredText.getFontMetricsInt(start, end, fmi);
+    }
+
+    /**
      * Returns a width of the character at the offset.
      *
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index be66db2..f31a690 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -21,6 +21,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.text.LineBreakConfig;
 import android.graphics.text.MeasuredText;
@@ -98,7 +99,7 @@
         private final @Layout.HyphenationFrequency int mHyphenationFrequency;
 
         // The line break configuration for calculating text wrapping.
-        private final @Nullable LineBreakConfig mLineBreakConfig;
+        private final @NonNull LineBreakConfig mLineBreakConfig;
 
         /**
          * A builder for creating {@link Params}.
@@ -118,7 +119,7 @@
                     Layout.HYPHENATION_FREQUENCY_NORMAL;
 
             // The line break configuration for calculating text wrapping.
-            private @Nullable LineBreakConfig mLineBreakConfig;
+            private @NonNull LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
 
             /**
              * Builder constructor.
@@ -211,7 +212,7 @@
         // For the external developers, use Builder instead.
         /** @hide */
         public Params(@NonNull TextPaint paint,
-                @Nullable LineBreakConfig lineBreakConfig,
+                @NonNull LineBreakConfig lineBreakConfig,
                 @NonNull TextDirectionHeuristic textDir,
                 @Layout.BreakStrategy int strategy,
                 @Layout.HyphenationFrequency int frequency) {
@@ -259,11 +260,12 @@
         }
 
         /**
-         * Return the line break configuration for this text.
+         * Returns the {@link LineBreakConfig} for this text.
          *
-         * @return the current line break configuration, null if no line break configuration is set.
+         * @return the current line break configuration. The {@link LineBreakConfig} with default
+         * values will be returned if no line break configuration is set.
          */
-        public @Nullable LineBreakConfig getLineBreakConfig() {
+        public @NonNull LineBreakConfig getLineBreakConfig() {
             return mLineBreakConfig;
         }
 
@@ -296,9 +298,9 @@
         /** @hide */
         public @CheckResultUsableResult int checkResultUsable(@NonNull TextPaint paint,
                 @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
-                @Layout.HyphenationFrequency int frequency, @Nullable LineBreakConfig lbConfig) {
+                @Layout.HyphenationFrequency int frequency, @NonNull LineBreakConfig lbConfig) {
             if (mBreakStrategy == strategy && mHyphenationFrequency == frequency
-                    && isLineBreakEquals(mLineBreakConfig, lbConfig)
+                    && mLineBreakConfig.equals(lbConfig)
                     && mPaint.equalsForTextMeasurement(paint)) {
                 return mTextDir == textDir ? USABLE : NEED_RECOMPUTE;
             } else {
@@ -307,29 +309,6 @@
         }
 
         /**
-         * Check the two LineBreakConfig instances are equal.
-         * This method assumes they are equal if one parameter is null and the other parameter has
-         * a LineBreakStyle value of LineBreakConfig.LINE_BREAK_STYLE_NONE.
-         *
-         * @param o1 the first LineBreakConfig instance.
-         * @param o2 the second LineBreakConfig instance.
-         * @return true if the two LineBreakConfig instances are equal.
-         */
-        private boolean isLineBreakEquals(LineBreakConfig o1, LineBreakConfig o2) {
-            if (Objects.equals(o1, o2)) {
-                return true;
-            }
-            if (o1 == null && (o2 != null
-                    && o2.getLineBreakStyle() == LineBreakConfig.LINE_BREAK_STYLE_NONE)) {
-                return true;
-            } else if (o2 == null && (o1 != null
-                    && o1.getLineBreakStyle() == LineBreakConfig.LINE_BREAK_STYLE_NONE)) {
-                return true;
-            }
-            return false;
-        }
-
-        /**
          * Check if the same text layout.
          *
          * @return true if this and the given param result in the same text layout
@@ -697,6 +676,38 @@
     }
 
     /**
+     * Retrieves the text font metrics for the given range.
+     * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
+     * IllegalArgumentException will be thrown.
+     *
+     * @param start the inclusive start offset in the text
+     * @param end the exclusive end offset in the text
+     * @param outMetrics the output font metrics
+     * @throws IllegalArgumentException if start and end offset are in the different paragraph.
+     */
+    public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Paint.FontMetricsInt outMetrics) {
+        Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
+        Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
+        Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
+        Objects.requireNonNull(outMetrics);
+        if (start == end) {
+            mParams.getTextPaint().getFontMetricsInt(outMetrics);
+            return;
+        }
+        final int paraIndex = findParaIndex(start);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        if (start < paraStart || paraEnd < end) {
+            throw new IllegalArgumentException("Cannot measured across the paragraph:"
+                    + "para: (" + paraStart + ", " + paraEnd + "), "
+                    + "request: (" + start + ", " + end + ")");
+        }
+        getMeasuredParagraph(paraIndex).getFontMetricsInt(start - paraStart,
+                end - paraStart, outMetrics);
+    }
+
+    /**
      * Returns a width of a character at offset
      *
      * @param offset an offset of the text.
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index b10fc37..d63d66e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -410,7 +410,8 @@
          *
          * @param lineBreakConfig the line break configuration for text wrapping.
          * @return this builder, useful for chaining.
-         * @see android.widget.TextView#setLineBreakConfig
+         * @see android.widget.TextView#setLineBreakStyle
+         * @see android.widget.TextView#setLineBreakWordStyle
          */
         @NonNull
         public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 49e2111..e39231c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -866,8 +866,12 @@
             wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl,
                     fmi);
         } else {
-            wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart, contextCount,
-                    runIsRtl, fmi);
+            if (mComputed == null) {
+                wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart,
+                        contextCount, runIsRtl, fmi);
+            } else {
+                mComputed.getFontMetricsInt(mStart + start, mStart + end, fmi);
+            }
         }
 
         updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 048b94f..ef21706 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -67,6 +67,13 @@
     public static final String SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME =
             "settings_app_allow_dark_theme_activation_at_bedtime";
 
+    /**
+     * Hide back key in the Settings two pane design.
+     * @hide
+     */
+    public static final String SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE =
+            "settings_hide_second_layer_page_navigate_up_button_in_two_pane";
+
     private static final Map<String, String> DEFAULT_FLAGS;
 
     static {
@@ -92,6 +99,7 @@
         DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
         DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "false");
+        DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
@@ -101,6 +109,7 @@
         PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
         PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
         PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
+        PERSISTENT_FLAGS.add(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE);
     }
 
     /**
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index ff04825..c5ab82d 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -18,13 +18,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.pm.Signature;
 import android.text.TextUtils;
 
 import libcore.util.HexEncoding;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.security.DigestInputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
@@ -35,6 +39,9 @@
  */
 public final class PackageUtils {
 
+    private static final int LOW_RAM_BUFFER_SIZE_BYTES = 1 * 1000;            // 1 kB
+    private static final int HIGH_RAM_BUFFER_SIZE_BYTES = 1 * 1000 * 1000;    // 1 MB
+
     private PackageUtils() {
         /* hide constructor */
     }
@@ -162,4 +169,55 @@
 
         return TextUtils.join(separator, pieces);
     }
+
+    /**
+     * @see #computeSha256DigestForLargeFile(String, String)
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath) {
+        return computeSha256DigestForLargeFile(filePath, null);
+    }
+
+    /**
+     * Computes the SHA256 digest of large files.
+     * @param filePath The path to which the file's content is to be hashed.
+     * @param separator Separator between each pair of characters, such as colon, or null to omit.
+     * @return The digest or null if an error occurs.
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
+            @Nullable String separator) {
+        MessageDigest messageDigest;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA256");
+            messageDigest.reset();
+        } catch (NoSuchAlgorithmException e) {
+            // this shouldn't happen!
+            return null;
+        }
+
+        boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
+        int bufferSize = isLowRamDevice ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES;
+
+        File f = new File(filePath);
+        try {
+            DigestInputStream digestStream = new DigestInputStream(new FileInputStream(f),
+                    messageDigest);
+            byte[] buffer = new byte[bufferSize];
+            while (digestStream.read(buffer) != -1);
+        } catch (IOException e) {
+            return null;
+        }
+
+        byte[] resultBytes = messageDigest.digest();
+
+        if (separator == null) {
+            return HexEncoding.encodeToString(resultBytes, true);
+        }
+
+        int length = resultBytes.length;
+        String[] pieces = new String[length];
+        for (int index = 0; index < length; index++) {
+            pieces[index] = HexEncoding.encodeToString(resultBytes[index], true);
+        }
+        return TextUtils.join(separator, pieces);
+    }
 }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index b8eb602..8e3cc34 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -499,9 +499,9 @@
      *
      * @param callback The extended frame callback to run during the next frame.
      *
-     * @see #removeExtendedFrameCallback
+     * @see #removeVsyncCallback
      */
-    public void postExtendedFrameCallback(@NonNull ExtendedFrameCallback callback) {
+    public void postVsyncCallback(@NonNull VsyncCallback callback) {
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
@@ -603,9 +603,9 @@
      *
      * @param callback The extended frame callback to remove.
      *
-     * @see #postExtendedFrameCallback
+     * @see #postVsyncCallback
      */
-    public void removeExtendedFrameCallback(@Nullable ExtendedFrameCallback callback) {
+    public void removeVsyncCallback(@Nullable VsyncCallback callback) {
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
@@ -1021,7 +1021,7 @@
          * The time in {@link System#nanoTime()} timebase which this frame is expected to be
          * presented.
          */
-        public long getExpectedPresentTimeNanos() {
+        public long getExpectedPresentationTimeNanos() {
             return mExpectedPresentTimeNanos;
         }
 
@@ -1034,7 +1034,7 @@
     }
 
     /**
-     * The payload for {@link ExtendedFrameCallback} which includes frame information such as when
+     * The payload for {@link VsyncCallback} which includes frame information such as when
      * the frame started being rendered, and multiple possible frame timelines and their
      * information including deadline and expected present time.
      */
@@ -1101,7 +1101,7 @@
      *
      * @see FrameCallback
      */
-    public interface ExtendedFrameCallback {
+    public interface VsyncCallback {
         /**
          * Called when a new display frame is being rendered.
          *
@@ -1214,7 +1214,7 @@
 
         void run(FrameData frameData) {
             if (token == EXTENDED_FRAME_CALLBACK_TOKEN) {
-                ((ExtendedFrameCallback) action).onVsync(frameData);
+                ((VsyncCallback) action).onVsync(frameData);
             } else {
                 run(frameData.getFrameTimeNanos());
             }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 36baa04..53b842a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -18,6 +18,7 @@
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.policy.IShortcutService;
 
 import android.app.IAssistDataReceiver;
@@ -65,7 +66,7 @@
 import android.view.SurfaceControl;
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
-import android.window.IOnFpsCallbackListener;
+import android.window.ITaskFpsCallback;
 
 /**
  * System private interface to the window manager.
@@ -199,6 +200,9 @@
     boolean isKeyguardSecure(int userId);
     void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message);
 
+    void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+    void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+
     // Requires INTERACT_ACROSS_USERS_FULL permission
     void setSwitchingUser(boolean switching);
 
@@ -926,21 +930,21 @@
      * registered, the registered callback will not be unregistered until
      * {@link unregisterTaskFpsCallback()} is called
      * @param taskId task id of the task.
-     * @param listener listener to be registered.
+     * @param callback callback to be registered.
      *
      * @hide
      */
-    void registerTaskFpsCallback(in int taskId, in IOnFpsCallbackListener listener);
+    void registerTaskFpsCallback(in int taskId, in ITaskFpsCallback callback);
 
     /**
      * Unregisters the frame rate per second count callback which was registered with
      * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
      *
-     * @param listener listener to be unregistered.
+     * @param callback callback to be unregistered.
      *
      * @hide
      */
-    void unregisterTaskFpsCallback(in IOnFpsCallbackListener listener);
+    void unregisterTaskFpsCallback(in ITaskFpsCallback listener);
 
     /**
      * Take a snapshot using the same path that's used for Recents. This is used for Testing only.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 3fc9f6b..b48b525 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -54,13 +54,11 @@
 
     @NonNull
     private InputMethodManagerDelegate getImmDelegate() {
-        InputMethodManagerDelegate delegate = mDelegate;
-        if (delegate != null) {
-            return delegate;
+        if (mDelegate == null) {
+            mDelegate = mViewRootImpl.mContext.getSystemService(
+                    InputMethodManager.class).getDelegate();
         }
-        delegate = mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
-        mDelegate = delegate;
-        return delegate;
+        return mDelegate;
     }
 
     /** Called when the view root is moved to a different display. */
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 188d745..7d56039 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -737,15 +737,47 @@
     }
 
     /**
-     * Gets the key code produced by the specified location on a US keyboard layout.
-     * Key code as defined in {@link android.view.KeyEvent}.
-     * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
-     * which can alter their key mapping using country specific keyboard layouts.
+     * Gets the {@link android.view.KeyEvent key code} produced by the given location on a reference
+     * QWERTY keyboard layout.
+     * <p>
+     * This API is useful for querying the physical location of keys that change the character
+     * produced based on the current locale and keyboard layout.
+     * <p>
+     * The following table provides a non-exhaustive list of examples:
+     * <table border="2" width="85%" align="center" cellpadding="5">
+     *     <thead>
+     *         <tr><th>Active Keyboard Layout</th> <th>Input Parameter</th>
+     *         <th>Return Value</th></tr>
+     *     </thead>
      *
-     * @param locationKeyCode The location of a key on a US keyboard layout.
-     * @return The key code produced when pressing the key at the specified location, given the
-     *         active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
-     *         mapping could not be determined, or if an error occurred.
+     *     <tbody>
+     *     <tr>
+     *         <td>French AZERTY</td>
+     *         <td><code>{@link KeyEvent#KEYCODE_Q}</code></td>
+     *         <td><code>{@link KeyEvent#KEYCODE_A}</code></td>
+     *     </tr>
+     *     <tr>
+     *         <td>German QWERTZ</td>
+     *         <td><code>{@link KeyEvent#KEYCODE_Y}</code></td>
+     *         <td><code>{@link KeyEvent#KEYCODE_Z}</code></td>
+     *     </tr>
+     *     <tr>
+     *         <td>US QWERTY</td>
+     *         <td><code>{@link KeyEvent#KEYCODE_B}</code></td>
+     *         <td><code>{@link KeyEvent#KEYCODE_B}</code></td>
+     *     </tr>
+     *     </tbody>
+     * </table>
+     *
+     * @param locationKeyCode The location of a key specified as a key code on the QWERTY layout.
+     * This provides a consistent way of referring to the physical location of a key independently
+     * of the current keyboard layout. Also see the
+     * <a href="https://www.w3.org/TR/2017/CR-uievents-code-20170601/#key-alphanumeric-writing-system">
+     * hypothetical keyboard</a> provided by the W3C, which may be helpful for identifying the
+     * physical location of a key.
+     * @return The key code produced by the key at the specified location, given the current
+     * keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the device does not specify
+     * {@link InputDevice#SOURCE_KEYBOARD} or the requested mapping cannot be determined.
      */
     public int getKeyCodeForKeyLocation(int locationKeyCode) {
         return InputManager.getInstance().getKeyCodeForKeyLocation(mId, locationKeyCode);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fce95c8..5500fb8 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -89,7 +89,9 @@
             ITYPE_BOTTOM_DISPLAY_CUTOUT,
             ITYPE_IME,
             ITYPE_CLIMATE_BAR,
-            ITYPE_EXTRA_NAVIGATION_BAR
+            ITYPE_EXTRA_NAVIGATION_BAR,
+            ITYPE_LOCAL_NAVIGATION_BAR_1,
+            ITYPE_LOCAL_NAVIGATION_BAR_2
     })
     public @interface InternalInsetsType {}
 
@@ -132,7 +134,11 @@
     public static final int ITYPE_CLIMATE_BAR = 20;
     public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
 
-    static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
+    /** Additional types for local insets. **/
+    public static final int ITYPE_LOCAL_NAVIGATION_BAR_1 = 22;
+    public static final int ITYPE_LOCAL_NAVIGATION_BAR_2 = 23;
+
+    static final int LAST_TYPE = ITYPE_LOCAL_NAVIGATION_BAR_2;
     public static final int SIZE = LAST_TYPE + 1;
 
     // Derived types
@@ -674,6 +680,8 @@
         if ((types & Type.NAVIGATION_BARS) != 0) {
             result.add(ITYPE_NAVIGATION_BAR);
             result.add(ITYPE_EXTRA_NAVIGATION_BAR);
+            result.add(ITYPE_LOCAL_NAVIGATION_BAR_1);
+            result.add(ITYPE_LOCAL_NAVIGATION_BAR_2);
         }
         if ((types & Type.CAPTION_BAR) != 0) {
             result.add(ITYPE_CAPTION_BAR);
@@ -714,6 +722,8 @@
                 return Type.STATUS_BARS;
             case ITYPE_NAVIGATION_BAR:
             case ITYPE_EXTRA_NAVIGATION_BAR:
+            case ITYPE_LOCAL_NAVIGATION_BAR_1:
+            case ITYPE_LOCAL_NAVIGATION_BAR_2:
                 return Type.NAVIGATION_BARS;
             case ITYPE_CAPTION_BAR:
                 return Type.CAPTION_BAR;
@@ -833,6 +843,10 @@
                 return "ITYPE_CLIMATE_BAR";
             case ITYPE_EXTRA_NAVIGATION_BAR:
                 return "ITYPE_EXTRA_NAVIGATION_BAR";
+            case ITYPE_LOCAL_NAVIGATION_BAR_1:
+                return "ITYPE_LOCAL_NAVIGATION_BAR_1";
+            case ITYPE_LOCAL_NAVIGATION_BAR_2:
+                return "ITYPE_LOCAL_NAVIGATION_BAR_2";
             default:
                 return "ITYPE_UNKNOWN_" + type;
         }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ce54968..98cef958 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -46,10 +46,13 @@
 import android.gui.DropInputMode;
 import android.hardware.DataSpace;
 import android.hardware.HardwareBuffer;
+import android.hardware.SyncFence;
 import android.hardware.display.DeviceProductInfo;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSync;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -209,7 +212,7 @@
     private static native void nativeReparent(long transactionObj, long nativeObject,
             long newParentNativeObject);
     private static native void nativeSetBuffer(long transactionObj, long nativeObject,
-            HardwareBuffer buffer);
+            HardwareBuffer buffer, long fencePtr);
     private static native void nativeSetBufferTransform(long transactionObj, long nativeObject,
             int transform);
     private static native void nativeSetDataSpace(long transactionObj, long nativeObject,
@@ -386,9 +389,7 @@
         public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
         // Either App or GPU took too long on the frame
         public static final int JANK_APP_DEADLINE_MISSED = 0x8;
-        // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
-        // jank
-        // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+        // Vsync predictions have drifted beyond the threshold from the actual HWVsync
         public static final int PREDICTION_ERROR = 0x10;
         // Latching a buffer early might cause an early present of the frame
         public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
@@ -3692,8 +3693,44 @@
          */
         public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc,
                 @Nullable HardwareBuffer buffer) {
+            return setBuffer(sc, buffer, null);
+        }
+
+        /**
+         * Updates the HardwareBuffer displayed for the SurfaceControl.
+         *
+         * Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY}
+         * as well as {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE} as the surface control might
+         * be composited using either an overlay or using the GPU.
+         *
+         * A presentation fence may be passed to improve performance by allowing the buffer
+         * to complete rendering while it is waiting for the transaction to be applied.
+         * For example, if the buffer is being produced by rendering with OpenGL ES then
+         * a fence created with
+         * {@link android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync)} can be
+         * used to allow the GPU rendering to be concurrent with the transaction. The compositor
+         * will wait for the fence to be signaled before the buffer is displayed. If multiple
+         * buffers are set as part of the same transaction, the presentation fences of all of them
+         * must signal before any buffer is displayed. That is, the entire transaction is delayed
+         * until all presentation fences have signaled, ensuring the transaction remains consistent.
+         *
+         * @param sc The SurfaceControl to update
+         * @param buffer The buffer to be displayed
+         * @param fence The presentation fence. If null or invalid, this is equivalent to
+         *              {@link #setBuffer(SurfaceControl, HardwareBuffer)}
+         * @return this
+         */
+        public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc,
+                @Nullable HardwareBuffer buffer, @Nullable SyncFence fence) {
             checkPreconditions(sc);
-            nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
+            if (fence != null) {
+                synchronized (fence.getLock()) {
+                    nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer,
+                            fence.getNativeFence());
+                }
+            } else {
+                nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer, 0);
+            }
             return this;
         }
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 1566f9e..785735c 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -184,6 +184,7 @@
      */
     public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark";
 
+    public static int EGL_CONTEXT_PRIORITY_REALTIME_NV = 0x3357;
     public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
     public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
     public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 179f6ee..286b502 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11785,7 +11785,10 @@
      * user and that ideally it should not be covered. Setting this is only appropriate for UI
      * where the user would likely take action to uncover it.
      * <p>
-     * The system will try to respect this, but when not possible will ignore it.
+     * The system will try to respect this preference, but when not possible will ignore it.
+     * <p>
+     * Note: while this is set to {@code true}, the system will ignore the {@code Rect}s provided
+     * through {@link #setPreferKeepClearRects} (but not clear them).
      * <p>
      * @see #setPreferKeepClearRects
      * @see #isPreferKeepClear
@@ -11817,11 +11820,11 @@
      * user and that ideally they should not be covered. Setting this is only appropriate for UI
      * where the user would likely take action to uncover it.
      * <p>
-     * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
-     * will be ignored.
-     * <p>
      * The system will try to respect this preference, but when not possible will ignore it.
      * <p>
+     * Note: While {@link #isPreferKeepClear} is {@code true}, the {@code Rect}s set here are
+     * ignored.
+     * <p>
      * @see #setPreferKeepClear
      * @see #getPreferKeepClearRects
      */
@@ -21139,6 +21142,10 @@
         }
 
         notifyEnterOrExitForAutoFillIfNeeded(false);
+
+        if (info != null && !collectPreferKeepClearRects().isEmpty()) {
+            info.mViewRootImpl.updateKeepClearRectsForView(this);
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8236fbb..8444032 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1201,7 +1201,7 @@
                         mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame);
                 setFrame(mTmpFrames.frame);
                 registerBackCallbackOnWindow();
-                if (WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+                if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
                     // For apps requesting legacy back behavior, we add a compat callback that
                     // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views.
                     // This way from system point of view, these apps are providing custom
@@ -6507,7 +6507,7 @@
 
             if (isBack(event)
                     && mContext != null
-                    && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+                    && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
                 // Invoke the appropriate {@link OnBackInvokedCallback} if the new back
                 // navigation should be used, and the key event is not handled by anything else.
                 OnBackInvokedCallback topCallback =
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 771d40b..8ee3e43 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3222,7 +3222,8 @@
 
         /**
          * The window is allowed to extend into the {@link DisplayCutout} area, only if the
-         * {@link DisplayCutout} is fully contained within a system bar. Otherwise, the window is
+         * {@link DisplayCutout} is fully contained within a system bar or the {@link DisplayCutout}
+         * is not deeper than 16 dp, but this depends on the OEM choice. Otherwise, the window is
          * laid out such that it does not overlap with the {@link DisplayCutout} area.
          *
          * <p>
@@ -3237,6 +3238,13 @@
          * The usual precautions for not overlapping with the status and navigation bar are
          * sufficient for ensuring that no important content overlaps with the DisplayCutout.
          *
+         * <p>
+         * Note: OEMs can have an option to allow the window to always extend into the
+         * {@link DisplayCutout} area, no matter the cutout flag set, when the {@link DisplayCutout}
+         * is on the different side from system bars, only if the {@link DisplayCutout} overlaps at
+         * most 16dp with the windows.
+         * In such case, OEMs must provide an opt-in/out affordance for users.
+         *
          * @see DisplayCutout
          * @see WindowInsets
          * @see #layoutInDisplayCutoutMode
@@ -3249,8 +3257,16 @@
          * The window is always allowed to extend into the {@link DisplayCutout} areas on the short
          * edges of the screen.
          *
+         * <p>
          * The window will never extend into a {@link DisplayCutout} area on the long edges of the
-         * screen.
+         * screen, unless the {@link DisplayCutout} is not deeper than 16 dp, but this depends on
+         * the OEM choice.
+         *
+         * <p>
+         * Note: OEMs can have an option to allow the window to extend into the
+         * {@link DisplayCutout} area on the long edge side, only if the cutout overlaps at most
+         * 16dp with the windows. In such case, OEMs must provide an opt-in/out affordance for
+         * users.
          *
          * <p>
          * The window must make sure that no important content overlaps with the
@@ -4865,21 +4881,24 @@
      * Registers the frame rate per second count callback for one given task ID.
      * Each callback can only register for receiving FPS callback for one task id until unregister
      * is called. If there's no task associated with the given task id,
-     * {@link IllegalArgumentException} will be thrown. If a task id destroyed after a callback is
-     * registered, the registered callback will not be unregistered until
-     * {@link #unregisterTaskFpsCallback(TaskFpsCallback))} is called
+     * {@link IllegalArgumentException} will be thrown. Registered callbacks should always be
+     * unregistered via {@link #unregisterTaskFpsCallback(TaskFpsCallback)}
+     * even when the task id has been destroyed.
+     *
      * @param taskId task id of the task.
+     * @param executor Executor to execute the callback.
      * @param callback callback to be registered.
      *
      * @hide
      */
     @SystemApi
     default void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
+            @NonNull Executor executor,
             @NonNull TaskFpsCallback callback) {}
 
     /**
      * Unregisters the frame rate per second count callback which was registered with
-     * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
+     * {@link #registerTaskFpsCallback(Executor, int, TaskFpsCallback)}.
      *
      * @param callback callback to be unregistered.
      *
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f4353eb..20cdad4 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -39,14 +39,18 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.window.ITaskFpsCallback;
 import android.window.TaskFpsCallback;
 import android.window.WindowContext;
 import android.window.WindowProvider;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -99,6 +103,10 @@
     @Nullable
     private final IBinder mWindowContextToken;
 
+    @GuardedBy("mOnFpsCallbackListenerProxies")
+    private final ArrayList<OnFpsCallbackListenerProxy> mOnFpsCallbackListenerProxies =
+            new ArrayList<>();
+
     public WindowManagerImpl(Context context) {
         this(context, null /* parentWindow */, null /* clientToken */);
     }
@@ -424,20 +432,56 @@
     }
 
     @Override
-    public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, TaskFpsCallback callback) {
+    public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, @NonNull Executor executor,
+            TaskFpsCallback callback) {
+        final OnFpsCallbackListenerProxy onFpsCallbackListenerProxy =
+                new OnFpsCallbackListenerProxy(executor, callback);
         try {
             WindowManagerGlobal.getWindowManagerService().registerTaskFpsCallback(
-                    taskId, callback.getListener());
+                    taskId, onFpsCallbackListenerProxy);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        synchronized (mOnFpsCallbackListenerProxies) {
+            mOnFpsCallbackListenerProxies.add(onFpsCallbackListenerProxy);
         }
     }
 
     @Override
     public void unregisterTaskFpsCallback(TaskFpsCallback callback) {
-        try {
-            WindowManagerGlobal.getWindowManagerService().unregisterTaskFpsCallback(
-                    callback.getListener());
-        } catch (RemoteException e) {
+        synchronized (mOnFpsCallbackListenerProxies) {
+            final Iterator<OnFpsCallbackListenerProxy> iterator =
+                    mOnFpsCallbackListenerProxies.iterator();
+            while (iterator.hasNext()) {
+                final OnFpsCallbackListenerProxy proxy = iterator.next();
+                if (proxy.mCallback == callback) {
+                    try {
+                        WindowManagerGlobal.getWindowManagerService()
+                                .unregisterTaskFpsCallback(proxy);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                    iterator.remove();
+                }
+            }
+        }
+    }
+
+    private static class OnFpsCallbackListenerProxy
+            extends ITaskFpsCallback.Stub {
+        private final Executor mExecutor;
+        private final TaskFpsCallback mCallback;
+
+        private OnFpsCallbackListenerProxy(Executor executor, TaskFpsCallback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onFpsReported(float fps) {
+            mExecutor.execute(() -> {
+                mCallback.onFpsReported(fps);
+            });
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index cd8dd86..6c647ce 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -385,6 +385,25 @@
  *   <li>{@link #getText()} - The text of the announcement.</li>
  * </ul>
  * </p>
+  * <p>
+ * <b>speechStateChanged</b>
+ * <em>Type:</em> {@link #TYPE_SPEECH_STATE_CHANGE}</br>
+ * Represents a change in the speech state defined by the
+ * bit mask of the speech state change types.
+ * A change in the speech state occurs when an application wants to signal that
+ * it is either speaking or listening for human speech.
+ * This event helps avoid conflicts where two applications want to speak or one listens
+ * when another speaks.
+ * When sending this event, the sender should ensure that  the accompanying state change types
+ * make sense. For example, the sender should not send
+ * {@link #SPEECH_STATE_SPEAKING_START} and {@link #SPEECH_STATE_SPEAKING_END} together.
+ * <em>Properties:</em></br>
+ * <ul>
+ *   <li>{@link #getSpeechStateChangeTypes()} - The type of state changes</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ * </ul>
+ * </p>
  *
  * @see android.view.accessibility.AccessibilityManager
  * @see android.accessibilityservice.AccessibilityService
@@ -553,14 +572,20 @@
     public static final int TYPE_ASSIST_READING_CONTEXT = 0x01000000;
 
     /**
-     * Represents a change in the speech state defined by the content-change types. A change in the
-     * speech state occurs when another service is either speaking or listening for human speech.
-     * This event helps avoid conflicts where two services want to speak or one listens
+     * Represents a change in the speech state defined by the speech state change types.
+     * A change in the speech state occurs when an application wants to signal that it is either
+     * speaking or listening for human speech.
+     * This event helps avoid conflicts where two applications want to speak or one listens
      * when another speaks.
+     * When sending this event, the sender should ensure that  the accompanying state change types
+     * make sense. For example, the sender should not send
+     * {@link #SPEECH_STATE_SPEAKING_START} and {@link #SPEECH_STATE_SPEAKING_END} together.
      * @see #SPEECH_STATE_SPEAKING_START
      * @see #SPEECH_STATE_SPEAKING_END
      * @see #SPEECH_STATE_LISTENING_START
      * @see #SPEECH_STATE_LISTENING_END
+     * @see #getSpeechStateChangeTypes
+     * @see #setSpeechStateChangeTypes
      */
     public static final int TYPE_SPEECH_STATE_CHANGE = 0x02000000;
 
@@ -1062,7 +1087,7 @@
     }
 
     /**
-     * Gets the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
+     * Gets the bit mask of the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
      *
      * @see #SPEECH_STATE_SPEAKING_START
      * @see #SPEECH_STATE_SPEAKING_END
@@ -1073,7 +1098,7 @@
         return mSpeechStateChangeTypes;
     }
 
-    private static String speechStateChangedTypesToString(int types) {
+    private static String speechStateChangeTypesToString(int types) {
         return BitUtils.flagsToString(
                 types, AccessibilityEvent::singleSpeechStateChangeTypeToString);
     }
@@ -1094,14 +1119,18 @@
     }
 
     /**
-     * Sets the speech state type signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
+     * Sets the bit mask of the speech state change types
+     * signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event.
+     * The sender is responsible for ensuring that  the state change types  make sense. For example,
+     * the sender should not send
+     * {@link #SPEECH_STATE_SPEAKING_START} and {@link #SPEECH_STATE_SPEAKING_END} together.
      *
      * @see #SPEECH_STATE_SPEAKING_START
      * @see #SPEECH_STATE_SPEAKING_END
      * @see #SPEECH_STATE_LISTENING_START
      * @see #SPEECH_STATE_LISTENING_END
      */
-    public void setSpeechStateChangeTypes(int state) {
+    public void setSpeechStateChangeTypes(@SpeechStateChangeTypes int state) {
         enforceNotSealed();
         mSpeechStateChangeTypes = state;
     }
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index a088632..108c42c 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -1,9 +1,7 @@
 # Bug component: 351486
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
 augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+joannechung@google.com
+markpun@google.com
+lpeter@google.com
+tymtsai@google.com
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index ad48ea0..48d2970 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -218,6 +218,7 @@
     public static final boolean DEBUG = false;
 
     /** @hide */
+    @TestApi
     public static final String DUMPABLE_NAME = "ContentCaptureManager";
 
     /** Error happened during the data sharing session. */
diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS
index 6337327..1a5cb1e4 100644
--- a/core/java/android/view/contentcapture/OWNERS
+++ b/core/java/android/view/contentcapture/OWNERS
@@ -1,9 +1,7 @@
 # Bug component: 544200
 
-joannechung@google.com
-adamhe@google.com
-tymtsai@google.com
-lpeter@google.com
 augale@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+joannechung@google.com
+markpun@google.com
+lpeter@google.com
+tymtsai@google.com
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 08cc31c..69ad739 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -391,22 +391,6 @@
     public void changeInputMethodSubtype(InputMethodSubtype subtype);
 
     /**
-     * Update token of the client window requesting {@link #showSoftInput(int, ResultReceiver)}
-     * @param showInputToken placeholder app window token for window requesting
-     *        {@link InputMethodManager#showSoftInput(View, int)}
-     * @hide
-     */
-    public void setCurrentShowInputToken(IBinder showInputToken);
-
-    /**
-     * Update token of the client window requesting {@link #hideSoftInput(int, ResultReceiver)}
-     * @param hideInputToken placeholder app window token for window requesting
-     *        {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}
-     * @hide
-     */
-    public void setCurrentHideInputToken(IBinder hideInputToken);
-
-    /**
      * Checks if IME is ready to start stylus handwriting session.
      * If yes, {@link #startStylusHandwriting(InputChannel, List)} is called.
      * @param requestId
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 9a70667..d7c1846 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -64,6 +64,7 @@
  * @attr ref android.R.styleable#InputMethod_isDefault
  * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
  * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
+ * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestionsWithTouchExploration
  * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
  * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
  * @attr ref android.R.styleable#InputMethod_configChanges
@@ -125,6 +126,11 @@
     private final boolean mInlineSuggestionsEnabled;
 
     /**
+     * The flag whether this IME supports inline suggestions when touch exploration is enabled.
+     */
+    private final boolean mSupportsInlineSuggestionsWithTouchExploration;
+
+    /**
      * The flag whether this IME suppresses spell checker.
      */
     private final boolean mSuppressesSpellChecker;
@@ -189,6 +195,7 @@
         boolean isAuxIme = true;
         boolean supportsSwitchingToNextInputMethod = false; // false as default
         boolean inlineSuggestionsEnabled = false; // false as default
+        boolean supportsInlineSuggestionsWithTouchExploration = false; // false as default
         boolean suppressesSpellChecker = false; // false as default
         boolean showInInputMethodPicker = true; // true as default
         mForceDefault = false;
@@ -234,6 +241,9 @@
                     false);
             inlineSuggestionsEnabled = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
+            supportsInlineSuggestionsWithTouchExploration = sa.getBoolean(
+                    com.android.internal.R.styleable
+                            .InputMethod_supportsInlineSuggestionsWithTouchExploration, false);
             suppressesSpellChecker = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
             showInInputMethodPicker = sa.getBoolean(
@@ -314,6 +324,8 @@
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
         mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+        mSupportsInlineSuggestionsWithTouchExploration =
+                supportsInlineSuggestionsWithTouchExploration;
         mSuppressesSpellChecker = suppressesSpellChecker;
         mShowInInputMethodPicker = showInInputMethodPicker;
         mIsVrOnly = isVrOnly;
@@ -326,6 +338,7 @@
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
         mInlineSuggestionsEnabled = source.readInt() == 1;
+        mSupportsInlineSuggestionsWithTouchExploration = source.readInt() == 1;
         mSuppressesSpellChecker = source.readBoolean();
         mShowInInputMethodPicker = source.readBoolean();
         mIsVrOnly = source.readBoolean();
@@ -345,7 +358,8 @@
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
-                0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
+                0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                false /* inlineSuggestionsEnabled */);
     }
 
     /**
@@ -360,7 +374,8 @@
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
                 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
-                false /* supportsStylusHandwriting */);
+                false /* supportsStylusHandwriting */,
+                false /* inlineSuggestionsEnabled */);
     }
 
     /**
@@ -373,7 +388,8 @@
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
                 true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
                 false /* isVrOnly */, 0 /* handledconfigChanges */,
-                false /* supportsStylusHandwriting */);
+                false /* supportsStylusHandwriting */,
+                false /* inlineSuggestionsEnabled */);
     }
 
     /**
@@ -385,7 +401,8 @@
             boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
                 supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
-                0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
+                0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
+                false /* inlineSuggestionsEnabled */);
     }
 
     /**
@@ -395,7 +412,8 @@
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
-            boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting) {
+            boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting,
+            boolean supportsInlineSuggestionsWithTouchExploration) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -406,6 +424,8 @@
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
         mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
+        mSupportsInlineSuggestionsWithTouchExploration =
+                supportsInlineSuggestionsWithTouchExploration;
         mSuppressesSpellChecker = false;
         mShowInInputMethodPicker = true;
         mIsVrOnly = isVrOnly;
@@ -583,6 +603,8 @@
                 + " mIsVrOnly=" + mIsVrOnly
                 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
                 + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+                + " mSupportsInlineSuggestionsWithTouchExploration="
+                + mSupportsInlineSuggestionsWithTouchExploration
                 + " mSuppressesSpellChecker=" + mSuppressesSpellChecker
                 + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
                 + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
@@ -654,6 +676,15 @@
     }
 
     /**
+     * Returns {@code true} if this input method supports inline suggestions when touch exploration
+     * is enabled.
+     * @hide
+     */
+    public boolean supportsInlineSuggestionsWithTouchExploration() {
+        return mSupportsInlineSuggestionsWithTouchExploration;
+    }
+
+    /**
      * Return {@code true} if this input method suppresses spell checker.
      */
     public boolean suppressesSpellChecker() {
@@ -683,6 +714,7 @@
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
         dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
+        dest.writeInt(mSupportsInlineSuggestionsWithTouchExploration ? 1 : 0);
         dest.writeBoolean(mSuppressesSpellChecker);
         dest.writeBoolean(mShowInInputMethodPicker);
         dest.writeBoolean(mIsVrOnly);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2359d8d..2717463 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2031,14 +2031,20 @@
      * @param inputConnection the connection to be invalidated.
      * @param textSnapshot {@link TextSnapshot} to be used to update {@link EditorInfo}.
      * @param sessionId the session ID to be sent.
+     * @return {@code true} if the operation is done. {@code false} if the caller needs to fall back
+     *         to {@link InputMethodManager#restartInput(View)}.
      * @hide
      */
-    public void doInvalidateInput(@NonNull RemoteInputConnectionImpl inputConnection,
+    public boolean doInvalidateInput(@NonNull RemoteInputConnectionImpl inputConnection,
             @NonNull TextSnapshot textSnapshot, int sessionId) {
         synchronized (mH) {
             if (mServedInputConnection != inputConnection || mCurrentTextBoxAttribute == null) {
                 // OK to ignore because the calling InputConnection is already abandoned.
-                return;
+                return true;
+            }
+            if (mCurrentInputMethodSession == null) {
+                // IME is not yet bound to the client.  Need to fall back to the restartInput().
+                return false;
             }
             final EditorInfo editorInfo = mCurrentTextBoxAttribute.createCopyInternal();
             editorInfo.initialSelStart = mCursorSelStart = textSnapshot.getSelectionStart();
@@ -2051,6 +2057,7 @@
                     sessionId);
             forAccessibilitySessions(wrapper -> wrapper.invalidateInput(editorInfo,
                     mServedInputConnection, sessionId));
+            return true;
         }
     }
 
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index d7db7c7..9fa7e8f 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -3,4 +3,4 @@
 
 include /services/core/java/com/android/server/inputmethod/OWNERS
 
-per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
+per-file *InlineSuggestion* = file:/core/java/android/view/autofill/OWNERS
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
index 6de0316..ef04b2c 100644
--- a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -105,7 +105,7 @@
 
     private boolean isRemoteSelectionToolbarEnabled() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SELECTION_TOOLBAR,
-                REMOTE_SELECTION_TOOLBAR_ENABLED, false);
+                REMOTE_SELECTION_TOOLBAR_ENABLED, true);
     }
 
     /**
diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS
index 4bcdeea..a205be2 100644
--- a/core/java/android/view/textclassifier/OWNERS
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -2,8 +2,6 @@
 
 mns@google.com
 toki@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
 augale@google.com
 joannechung@google.com
 tonymak@google.com
diff --git a/core/java/android/view/textclassifier/logging/OWNERS b/core/java/android/view/textclassifier/logging/OWNERS
deleted file mode 100644
index ac80d9f..0000000
--- a/core/java/android/view/textclassifier/logging/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Bug component: 709498
-
-mns@google.com
-toki@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-augale@google.com
-joannechung@google.com
diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS
index a1e663a..b772ad3 100644
--- a/core/java/android/view/translation/OWNERS
+++ b/core/java/android/view/translation/OWNERS
@@ -1,8 +1,7 @@
 # Bug component: 994311
 
-adamhe@google.com
 augale@google.com
 joannechung@google.com
+markpun@google.com
 lpeter@google.com
-svetoslavganov@google.com
 tymtsai@google.com
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 5bcfa8b..1da2af4 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.S;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index cf6807e..0ef37d1 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,7 +51,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForS";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3dfb4a5..dbaf0aa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -788,7 +788,8 @@
     private Layout mLayout;
     private boolean mLocalesChanged = false;
     private int mTextSizeUnit = -1;
-    private LineBreakConfig mLineBreakConfig = new LineBreakConfig();
+    private int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
+    private int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
 
     // This is used to reflect the current user preference for changing font weight and making text
     // more bold.
@@ -1457,13 +1458,12 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_lineBreakStyle:
-                    mLineBreakConfig.setLineBreakStyle(
-                            a.getInt(attr, LineBreakConfig.LINE_BREAK_STYLE_NONE));
+                    mLineBreakStyle = a.getInt(attr, LineBreakConfig.LINE_BREAK_STYLE_NONE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
-                    mLineBreakConfig.setLineBreakWordStyle(
-                            a.getInt(attr, LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE));
+                    mLineBreakWordStyle = a.getInt(attr,
+                            LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeTextType:
@@ -4301,13 +4301,12 @@
             @LineBreakConfig.LineBreakStyle int lineBreakStyle,
             @LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
         boolean updated = false;
-        if (isLineBreakStyleSpecified && mLineBreakConfig.getLineBreakStyle() != lineBreakStyle) {
-            mLineBreakConfig.setLineBreakStyle(lineBreakStyle);
+        if (isLineBreakStyleSpecified && mLineBreakStyle != lineBreakStyle) {
+            mLineBreakStyle = lineBreakStyle;
             updated = true;
         }
-        if (isLineBreakWordStyleSpecified
-                && mLineBreakConfig.getLineBreakWordStyle() != lineBreakWordStyle) {
-            mLineBreakConfig.setLineBreakWordStyle(lineBreakWordStyle);
+        if (isLineBreakWordStyleSpecified && mLineBreakWordStyle != lineBreakWordStyle) {
+            mLineBreakWordStyle = lineBreakWordStyle;
             updated = true;
         }
         if (updated && mLayout != null) {
@@ -4871,50 +4870,72 @@
     }
 
     /**
-     * Sets line break configuration indicates which strategy needs to be used when calculating the
-     * text wrapping.
-     * <P>
-     * There are two types of line break rules that can be configured at the same time. One is
-     * line break style(lb) and the other is line break word style(lw). The line break style
-     * affects rule-based breaking. The line break word style affects dictionary-based breaking
-     * and provide phrase-based breaking opportunities. There are several types for the
-     * line break style:
+     * Set the line break style for text wrapping.
+     *
+     * The line break style to indicates the line break strategies can be used when
+     * calculating the text wrapping. The line break style affects rule-based breaking. It
+     * specifies the strictness of line-breaking rules.
+     * There are several types for the line break style:
      * {@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE},
      * {@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL} and
-     * {@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}.
-     * The type for the line break word style is
-     * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE}.
-     * The default values of the line break style and the line break word style are
-     * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and
-     * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE} respectively, indicating that no line
-     * breaking rules are specified.
-     * See <a href="https://drafts.csswg.org/css-text/#line-break-property">
+     * {@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}. The default values of the line break style
+     * is {@link LineBreakConfig#LINE_BREAK_STYLE_NONE}, indicating no breaking rule is specified.
+     * See <a href="https://www.w3.org/TR/css-text-3/#line-break-property">
      *         the line-break property</a>
      *
-     * @param lineBreakConfig the line break config for text wrapping.
+     * @param lineBreakStyle the line break style for the text.
      */
-    public void setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
-        Objects.requireNonNull(lineBreakConfig);
-        if (mLineBreakConfig.equals(lineBreakConfig)) {
-            return;
-        }
-        mLineBreakConfig.set(lineBreakConfig);
-        if (mLayout != null) {
-            nullLayouts();
-            requestLayout();
-            invalidate();
+    public void setLineBreakStyle(@LineBreakConfig.LineBreakStyle int lineBreakStyle) {
+        if (mLineBreakStyle != lineBreakStyle) {
+            mLineBreakStyle = lineBreakStyle;
+            if (mLayout != null) {
+                nullLayouts();
+                requestLayout();
+                invalidate();
+            }
         }
     }
 
     /**
-     * Get the current line break configuration for text wrapping.
+     * Set the line break word style for text wrapping.
      *
-     * @return the current line break configuration to be used for text wrapping.
+     * The line break word style affects dictionary-based breaking and provide phrase-based
+     * breaking opportunities. The type for the line break word style is
+     * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE}. The default values of the line break
+     * word style is {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}, indicating no breaking rule
+     * is specified.
+     * See <a href="https://www.w3.org/TR/css-text-3/#word-break-property">
+     *         the word-break property</a>
+     *
+     * @param lineBreakWordStyle the line break word style for the tet
      */
-    public @NonNull LineBreakConfig getLineBreakConfig() {
-        LineBreakConfig lbConfig = new LineBreakConfig();
-        lbConfig.set(mLineBreakConfig);
-        return lbConfig;
+    public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
+        if (mLineBreakWordStyle != lineBreakWordStyle) {
+            mLineBreakWordStyle = lineBreakWordStyle;
+            if (mLayout != null) {
+                nullLayouts();
+                requestLayout();
+                invalidate();
+            }
+        }
+    }
+
+    /**
+     * Get the current line break style for text wrapping.
+     *
+     * @return the current line break style to be used for text wrapping.
+     */
+    public @LineBreakConfig.LineBreakStyle int getLineBreakStyle() {
+        return mLineBreakStyle;
+    }
+
+    /**
+     * Get the current line word break style for text wrapping.
+     *
+     * @return the current line break word style to be used for text wrapping.
+     */
+    public @LineBreakConfig.LineBreakWordStyle int getLineBreakWordStyle() {
+        return mLineBreakWordStyle;
     }
 
     /**
@@ -4924,7 +4945,8 @@
      * @see PrecomputedText
      */
     public @NonNull PrecomputedText.Params getTextMetricsParams() {
-        return new PrecomputedText.Params(new TextPaint(mTextPaint), mLineBreakConfig,
+        return new PrecomputedText.Params(new TextPaint(mTextPaint),
+                LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle),
                 getTextDirectionHeuristic(),
                 mBreakStrategy, mHyphenationFrequency);
     }
@@ -4941,13 +4963,9 @@
         mTextDir = params.getTextDirection();
         mBreakStrategy = params.getBreakStrategy();
         mHyphenationFrequency = params.getHyphenationFrequency();
-        if (params.getLineBreakConfig() != null) {
-            mLineBreakConfig.set(params.getLineBreakConfig());
-        } else {
-            // Set default value if the line break config in the PrecomputedText.Params is null.
-            mLineBreakConfig.setLineBreakStyle(DEFAULT_LINE_BREAK_STYLE);
-            mLineBreakConfig.setLineBreakWordStyle(DEFAULT_LINE_BREAK_WORD_STYLE);
-        }
+        LineBreakConfig lineBreakConfig = params.getLineBreakConfig();
+        mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
+        mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
         if (mLayout != null) {
             nullLayouts();
             requestLayout();
@@ -6486,7 +6504,8 @@
             }
             final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
                     precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
-                            mHyphenationFrequency, mLineBreakConfig);
+                            mHyphenationFrequency, LineBreakConfig.getLineBreakConfig(
+                                    mLineBreakStyle, mLineBreakWordStyle));
             switch (checkResult) {
                 case PrecomputedText.Params.UNUSABLE:
                     throw new IllegalArgumentException(
@@ -9383,7 +9402,8 @@
                         .setHyphenationFrequency(mHyphenationFrequency)
                         .setJustificationMode(mJustificationMode)
                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
-                        .setLineBreakConfig(mLineBreakConfig);
+                        .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
+                                mLineBreakStyle, mLineBreakWordStyle));
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
                             .setEllipsizedWidth(ellipsisWidth);
@@ -9498,7 +9518,8 @@
                     .setHyphenationFrequency(mHyphenationFrequency)
                     .setJustificationMode(mJustificationMode)
                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
-                    .setLineBreakConfig(mLineBreakConfig);
+                    .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
+                            mLineBreakStyle, mLineBreakWordStyle));
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
                         .setEllipsizedWidth(ellipsisWidth);
@@ -9866,7 +9887,8 @@
                 .setJustificationMode(getJustificationMode())
                 .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                 .setTextDirection(getTextDirectionHeuristic())
-                .setLineBreakConfig(mLineBreakConfig);
+                .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
+                        mLineBreakStyle, mLineBreakWordStyle));
 
         final StaticLayout layout = layoutBuilder.build();
 
diff --git a/core/java/android/widget/inline/OWNERS b/core/java/android/widget/inline/OWNERS
new file mode 100644
index 0000000..9a30e82
--- /dev/null
+++ b/core/java/android/widget/inline/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 351486
+
+include /core/java/android/view/autofill/OWNERS
diff --git a/core/java/android/window/IOnFpsCallbackListener.aidl b/core/java/android/window/ITaskFpsCallback.aidl
similarity index 95%
rename from core/java/android/window/IOnFpsCallbackListener.aidl
rename to core/java/android/window/ITaskFpsCallback.aidl
index 3091df3..aee403e9 100644
--- a/core/java/android/window/IOnFpsCallbackListener.aidl
+++ b/core/java/android/window/ITaskFpsCallback.aidl
@@ -19,7 +19,7 @@
 /**
  * @hide
  */
-oneway interface IOnFpsCallbackListener {
+oneway interface ITaskFpsCallback {
 
     /**
      * Reports the fps from the registered task
diff --git a/core/java/android/window/TaskFpsCallback.java b/core/java/android/window/TaskFpsCallback.java
index a8e01b6..19b9f28 100644
--- a/core/java/android/window/TaskFpsCallback.java
+++ b/core/java/android/window/TaskFpsCallback.java
@@ -21,8 +21,6 @@
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 
-import java.util.concurrent.Executor;
-
 /**
  * Callback for sampling the frames per second for a task and its children.
  * This should only be used by a system component that needs to listen to a task's
@@ -30,44 +28,19 @@
  * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
  *
  * Each callback can only register for receiving FPS report for one task id until
- * {@link WindowManager#unregister()} is called.
+ * {@link WindowManager#unregisterTaskFpsCallback()} is called.
  *
  * @hide
  */
 @SystemApi
-public final class TaskFpsCallback {
+public abstract class TaskFpsCallback {
 
     /**
-     * Listener interface to receive frame per second of a task.
+     * Reports the fps from the registered task
+     * @param fps The frame per second of the task that has the registered task id
+     *            and its children.
      */
-    public interface OnFpsCallbackListener {
-        /**
-         * Reports the fps from the registered task
-         * @param fps The frame per second of the task that has the registered task id
-         *            and its children.
-         */
-        void onFpsReported(float fps);
-    }
-
-    private final IOnFpsCallbackListener mListener;
-
-    public TaskFpsCallback(@NonNull Executor executor, @NonNull OnFpsCallbackListener listener) {
-        mListener = new IOnFpsCallbackListener.Stub() {
-            @Override
-            public void onFpsReported(float fps) {
-                executor.execute(() -> {
-                    listener.onFpsReported(fps);
-                });
-            }
-        };
-    }
-
-    /**
-     * @hide
-     */
-    public IOnFpsCallbackListener getListener() {
-        return mListener;
-    }
+    public abstract void onFpsReported(float fps);
 
     /**
      * Dispatch the collected sample.
@@ -76,7 +49,7 @@
      */
     @BinderThread
     private static void dispatchOnFpsReported(
-            @NonNull IOnFpsCallbackListener listener, float fps) {
+            @NonNull ITaskFpsCallback listener, float fps) {
         try {
             listener.onFpsReported(fps);
         } catch (RemoteException e) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index aaaf729..7718a3b 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -364,8 +364,13 @@
         private final Point mEndRelOffset = new Point();
         private ActivityManager.RunningTaskInfo mTaskInfo = null;
         private boolean mAllowEnterPip;
-        private int mStartRotation = ROTATION_UNDEFINED;
-        private int mEndRotation = ROTATION_UNDEFINED;
+        private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED;
+        private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED;
+        /**
+         * The end rotation of the top activity after fixed rotation is finished. If the top
+         * activity is not in fixed rotation, it will be {@link ROTATION_UNDEFINED}.
+         */
+        private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED;
         private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
         private @ColorInt int mBackgroundColor;
 
@@ -388,6 +393,7 @@
             mAllowEnterPip = in.readBoolean();
             mStartRotation = in.readInt();
             mEndRotation = in.readInt();
+            mEndFixedRotation = in.readInt();
             mRotationAnimation = in.readInt();
             mBackgroundColor = in.readInt();
         }
@@ -441,6 +447,11 @@
             mEndRotation = end;
         }
 
+        /** Sets end rotation that top activity will be launched to after fixed rotation. */
+        public void setEndFixedRotation(@Surface.Rotation int endFixedRotation) {
+            mEndFixedRotation = endFixedRotation;
+        }
+
         /**
          * Sets the app-requested animation type for rotation. Will be one of the
          * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams};
@@ -521,14 +532,21 @@
             return mAllowEnterPip;
         }
 
+        @Surface.Rotation
         public int getStartRotation() {
             return mStartRotation;
         }
 
+        @Surface.Rotation
         public int getEndRotation() {
             return mEndRotation;
         }
 
+        @Surface.Rotation
+        public int getEndFixedRotation() {
+            return mEndFixedRotation;
+        }
+
         /** @return the rotation animation. */
         public int getRotationAnimation() {
             return mRotationAnimation;
@@ -555,6 +573,7 @@
             dest.writeBoolean(mAllowEnterPip);
             dest.writeInt(mStartRotation);
             dest.writeInt(mEndRotation);
+            dest.writeInt(mEndFixedRotation);
             dest.writeInt(mRotationAnimation);
             dest.writeInt(mBackgroundColor);
         }
@@ -584,7 +603,8 @@
             return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
                     + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
                     + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
-                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
+                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation
+                    + " endFixedRotation=" + mEndFixedRotation + "}";
         }
     }
 
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 17b675f..5007df5 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -16,16 +16,22 @@
 
 package android.window;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+
 /**
  * The controller to manage {@link WindowContext}, such as attaching to a window manager node or
  * detaching from the current attached node. The user must call
@@ -35,13 +41,43 @@
  * @hide
  */
 public class WindowContextController {
+    // TODO(220049234): Disable attach debug logging before shipping.
+    private static final boolean DEBUG_ATTACH = true;
+    private static final String TAG = "WindowContextController";
+
     /**
-     * {@code true} to indicate that the {@code mToken} is associated with a
+     * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a
      * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
-     * WindowToken after this flag sets to {@code true}.
+     * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}.
      */
     @VisibleForTesting
-    public boolean mAttachedToDisplayArea;
+    public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED;
+
+    /**
+     * Status to indicate that the Window Context attach to a
+     * {@link com.android.server.wm.DisplayArea}.
+     */
+    @Retention(SOURCE)
+    @IntDef({AttachStatus.STATUS_INITIALIZED, AttachStatus.STATUS_ATTACHED,
+            AttachStatus.STATUS_DETACHED, AttachStatus.STATUS_FAILED})
+    public @interface AttachStatus{
+        /**
+         * The Window Context haven't attached to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_INITIALIZED = 0;
+        /**
+         * The Window Context has already attached to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_ATTACHED = 1;
+        /**
+         * The Window Context has detached from a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_DETACHED = 2;
+        /**
+         * The Window Context fails to attach to a {@link com.android.server.wm.DisplayArea}.
+         */
+        int STATUS_FAILED = 3;
+    }
     @NonNull
     private final WindowTokenClient mToken;
 
@@ -65,11 +101,19 @@
      * DisplayArea.
      */
     public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) {
-        if (mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("A Window Context can be only attached to "
                     + "a DisplayArea once.");
         }
-        mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options);
+        mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options)
+                ? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
+            Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
+                    + displayId);
+        } else if (DEBUG_ATTACH) {
+            Log.d(TAG, "attachToDisplayArea success, type:" + type + ", displayId:"
+                    + displayId);
+        }
     }
 
     /**
@@ -93,18 +137,21 @@
      * @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder)
      */
     public void attachToWindowToken(IBinder windowToken) {
-        if (!mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("The Window Context should have been attached"
-                    + " to a DisplayArea.");
+                    + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
         }
         mToken.attachToWindowToken(windowToken);
     }
 
     /** Detaches the window context from the node it's currently associated with. */
     public void detachIfNeeded() {
-        if (mAttachedToDisplayArea) {
+        if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
             mToken.detachFromWindowContainerIfNeeded();
-            mAttachedToDisplayArea = false;
+            mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
+            if (DEBUG_ATTACH) {
+                Log.d(TAG, "Detach Window Context.");
+            }
         }
     }
 }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 62292f97..0503c40 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -19,9 +19,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
+import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -50,10 +52,9 @@
     private IWindowSession mWindowSession;
     private IWindow mWindow;
     private static final String TAG = "WindowOnBackDispatcher";
-    private static final boolean DEBUG = false;
     private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
     private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+            .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
 
     /** Convenience hashmap to quickly decide if a callback has been added. */
     private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -227,10 +228,23 @@
      *
      * Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered
      * {@link android.view.OnBackInvokedCallback}.
-     *
      */
-    public static boolean shouldUseLegacyBack() {
-        return !CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME)
-                || !IS_BACK_PREDICTABILITY_ENABLED;
+    public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
+        // new back is enabled if the app targets T AND the feature flag is enabled AND the app
+        // does not explicitly request legacy back.
+        boolean targetsT = CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME);
+        boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED;
+        // If the context is null, we assume true and fallback on the two other conditions.
+        boolean appRequestsLegacy =
+                context == null || !context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
+
+        if (DEBUG) {
+            Log.d(TAG, TextUtils.formatSimple("App: %s isChangeEnabled=%s featureFlagEnabled=%s "
+                            + "onBackInvokedEnabled=%s",
+                    context != null ? context.getApplicationInfo().packageName : "null context",
+                    targetsT, featureFlagEnabled, !appRequestsLegacy));
+        }
+
+        return targetsT && featureFlagEnabled && !appRequestsLegacy;
     }
 }
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 7108d14..23d966fd 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -16,25 +16,26 @@
 
 package com.android.internal.app;
 
-import com.android.internal.R;
-
-import android.app.Dialog;
+import android.app.AlertDialog;
 import android.content.Context;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.TypedValue;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
+import com.android.internal.R;
+
 import java.util.Comparator;
 
 /**
@@ -48,9 +49,10 @@
  *
  * TODO: Move this back into the API, as in the support library media router.
  */
-public class MediaRouteChooserDialog extends Dialog {
+public class MediaRouteChooserDialog extends AlertDialog {
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
+    private final boolean mShowProgressBarWhenEmpty;
 
     private int mRouteTypes;
     private View.OnClickListener mExtendedSettingsClickListener;
@@ -60,10 +62,15 @@
     private boolean mAttachedToWindow;
 
     public MediaRouteChooserDialog(Context context, int theme) {
+        this(context, theme, true);
+    }
+
+    public MediaRouteChooserDialog(Context context, int theme, boolean showProgressBarWhenEmpty) {
         super(context, theme);
 
         mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
         mCallback = new MediaRouterCallback();
+        mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
     }
 
     /**
@@ -120,28 +127,38 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+        // Note: setView must be called before super.onCreate().
+        setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
+                null));
 
-        getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
-
-        setContentView(R.layout.media_route_chooser_dialog);
         setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
                 ? R.string.media_route_chooser_title_for_remote_display
                 : R.string.media_route_chooser_title);
 
-        // Must be called after setContentView.
-        getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
-                isLightTheme(getContext()) ? R.drawable.ic_media_route_off_holo_light
-                    : R.drawable.ic_media_route_off_holo_dark);
+        setIcon(isLightTheme(getContext()) ? R.drawable.ic_media_route_off_holo_light
+                : R.drawable.ic_media_route_off_holo_dark);
 
+        super.onCreate(savedInstanceState);
+
+        View emptyView = findViewById(android.R.id.empty);
         mAdapter = new RouteAdapter(getContext());
-        mListView = (ListView)findViewById(R.id.media_route_list);
+        mListView = (ListView) findViewById(R.id.media_route_list);
         mListView.setAdapter(mAdapter);
         mListView.setOnItemClickListener(mAdapter);
-        mListView.setEmptyView(findViewById(android.R.id.empty));
+        mListView.setEmptyView(emptyView);
 
-        mExtendedSettingsButton = (Button)findViewById(R.id.media_route_extended_settings_button);
+        mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
         updateExtendedSettingsButton();
+
+        if (!mShowProgressBarWhenEmpty) {
+            findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+            // Center the empty view when the progress bar is not shown.
+            LinearLayout.LayoutParams params =
+                    (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+            params.gravity = Gravity.CENTER;
+            emptyView.setLayoutParams(params);
+        }
     }
 
     private void updateExtendedSettingsButton() {
diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
index bb2d7fa..5628b7e 100644
--- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
+++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
@@ -66,24 +66,37 @@
         }
     }
 
+    /** Create a media route dialog as appropriate. */
     public static Dialog createDialog(Context context,
             int routeTypes, View.OnClickListener extendedSettingsClickListener) {
-        final MediaRouter router = (MediaRouter)context.getSystemService(
-                Context.MEDIA_ROUTER_SERVICE);
-
         int theme = MediaRouteChooserDialog.isLightTheme(context)
                 ? android.R.style.Theme_DeviceDefault_Light_Dialog
                 : android.R.style.Theme_DeviceDefault_Dialog;
+        return createDialog(context, routeTypes, extendedSettingsClickListener, theme);
+    }
+
+    /** Create a media route dialog as appropriate. */
+    public static Dialog createDialog(Context context,
+            int routeTypes, View.OnClickListener extendedSettingsClickListener, int theme) {
+        return createDialog(context, routeTypes, extendedSettingsClickListener, theme,
+                true /* showProgressBarWhenEmpty */);
+    }
+
+    /** Create a media route dialog as appropriate. */
+    public static Dialog createDialog(Context context, int routeTypes,
+            View.OnClickListener extendedSettingsClickListener, int theme,
+            boolean showProgressBarWhenEmpty) {
+        final MediaRouter router = context.getSystemService(MediaRouter.class);
 
         MediaRouter.RouteInfo route = router.getSelectedRoute();
         if (route.isDefault() || !route.matchesTypes(routeTypes)) {
-            final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme);
+            final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme,
+                    showProgressBarWhenEmpty);
             d.setRouteTypes(routeTypes);
             d.setExtendedSettingsClickListener(extendedSettingsClickListener);
             return d;
         } else {
-            MediaRouteControllerDialog d = new MediaRouteControllerDialog(context, theme);
-            return d;
+            return new MediaRouteControllerDialog(context, theme);
         }
     }
 }
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 0443ad0..0835824 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -455,7 +455,14 @@
             if (mSourceU != null) {
                 // T done
                 mResultT = (T) res;
-                mSourceU.whenComplete(this);
+
+                // Subscribe to the second job completion.
+                mSourceU.whenComplete((r, e) -> {
+                    // Mark the first job completion by setting mSourceU to null, so that next time
+                    // the execution flow goes to the else case below.
+                    mSourceU = null;
+                    accept(r, e);
+                });
             } else {
                 // U done
                 try {
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index d1942ac..780de0e 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -282,9 +282,8 @@
 
                     if (!alwaysTrueEndBatchEditDetected) {
                         final TextSnapshot textSnapshot = ic.takeSnapshot();
-                        if (textSnapshot != null) {
-                            mParentInputMethodManager.doInvalidateInput(this, textSnapshot,
-                                    nextSessionId);
+                        if (textSnapshot != null && mParentInputMethodManager.doInvalidateInput(
+                                this, textSnapshot, nextSessionId)) {
                             return;
                         }
                     }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index e1a67d8..34c47ed 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -98,6 +98,7 @@
     private final Handler mHandler;
     private final ChoreographerWrapper mChoreographer;
     private final Object mLock = InteractionJankMonitor.getInstance().getLock();
+    private final boolean mDeferMonitoring;
 
     @VisibleForTesting
     public final boolean mSurfaceOnly;
@@ -153,6 +154,7 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mSurfaceControlWrapper = surfaceControlWrapper;
+        mDeferMonitoring = config.shouldDeferMonitor();
 
         // HWUI instrumentation init.
         mRendererWrapper = mSurfaceOnly ? null : renderer;
@@ -228,12 +230,25 @@
      */
     public void begin() {
         synchronized (mLock) {
-            mBeginVsyncId = mChoreographer.getVsyncId() + 1;
+            final long currentVsync = mChoreographer.getVsyncId();
+            // In normal case, we should begin at the next frame,
+            // the id of the next frame is not simply increased by 1,
+            // but we can exclude the current frame at least.
+            mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
             if (DEBUG) {
-                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
+                Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
+                        + ", defer=" + mDeferMonitoring);
             }
             if (mSurfaceControl != null) {
-                postTraceStartMarker();
+                if (mDeferMonitoring) {
+                    // Normal case, we begin the instrument from the very beginning,
+                    // except the first frame.
+                    postTraceStartMarker();
+                } else {
+                    // If we don't begin the instrument from the very beginning,
+                    // there is no need to skip the frame where the begin invocation happens.
+                    beginInternal();
+                }
                 mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
             }
             if (!mSurfaceOnly) {
@@ -247,15 +262,18 @@
      */
     @VisibleForTesting
     public void postTraceStartMarker() {
-        mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, () -> {
-            synchronized (mLock) {
-                if (mCancelled || mEndVsyncId != INVALID_ID) {
-                    return;
-                }
-                mTracingStarted = true;
-                Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+        mChoreographer.mChoreographer.postCallback(
+                Choreographer.CALLBACK_INPUT, this::beginInternal, null);
+    }
+
+    private void beginInternal() {
+        synchronized (mLock) {
+            if (mCancelled || mEndVsyncId != INVALID_ID) {
+                return;
             }
-        }, null);
+            mTracingStarted = true;
+            Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+        }
     }
 
     /**
@@ -464,8 +482,7 @@
             if (info.surfaceControlCallbackFired) {
                 totalFramesCount++;
                 boolean missedFrame = false;
-                if ((info.jankType & PREDICTION_ERROR) != 0
-                        || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
+                if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
                     Log.w(TAG, "Missed App frame:" + info.jankType);
                     missedAppFramesCount++;
                     missedFrame = true;
@@ -473,7 +490,8 @@
                 if ((info.jankType & DISPLAY_HAL) != 0
                         || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
                         || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
-                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0) {
+                        || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
+                        || (info.jankType & PREDICTION_ERROR) != 0) {
                     Log.w(TAG, "Missed SF frame:" + info.jankType);
                     missedSfFramesCount++;
                     missedFrame = true;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 5a66e9a..3746bfd 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -717,6 +717,7 @@
         private final boolean mSurfaceOnly;
         private final SurfaceControl mSurfaceControl;
         private final @CujType int mCujType;
+        private final boolean mDeferMonitor;
 
         /**
          * A builder for building Configuration. {@link #setView(View)} is essential
@@ -733,6 +734,7 @@
             private boolean mAttrSurfaceOnly;
             private SurfaceControl mAttrSurfaceControl;
             private @CujType int mAttrCujType;
+            private boolean mAttrDeferMonitor = true;
 
             /**
              * Creates a builder which instruments only surface.
@@ -823,6 +825,16 @@
             }
 
             /**
+             * Indicates if the instrument should be deferred to the next frame.
+             * @param defer true if the instrument should be deferred to the next frame.
+             * @return builder
+             */
+            public Builder setDeferMonitorForAnimationStart(boolean defer) {
+                mAttrDeferMonitor = defer;
+                return this;
+            }
+
+            /**
              * Builds the {@link Configuration} instance
              * @return the instance of {@link Configuration}
              * @throws IllegalArgumentException if any invalid attribute is set
@@ -830,12 +842,14 @@
             public Configuration build() throws IllegalArgumentException {
                 return new Configuration(
                         mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
-                        mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl);
+                        mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl,
+                        mAttrDeferMonitor);
             }
         }
 
         private Configuration(@CujType int cuj, View view, String tag, long timeout,
-                boolean surfaceOnly, Context context, SurfaceControl surfaceControl) {
+                boolean surfaceOnly, Context context, SurfaceControl surfaceControl,
+                boolean deferMonitor) {
             mCujType = cuj;
             mTag = tag;
             mTimeout = timeout;
@@ -845,6 +859,7 @@
                     ? context
                     : (view != null ? view.getContext().getApplicationContext() : null);
             mSurfaceControl = surfaceControl;
+            mDeferMonitor = deferMonitor;
             validate();
         }
 
@@ -901,6 +916,13 @@
         Context getContext() {
             return mContext;
         }
+
+        /**
+         * @return true if the monitoring should be deferred to the next frame, false otherwise.
+         */
+        public boolean shouldDeferMonitor() {
+            return mDeferMonitor;
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 70b9639..55c2f4c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5225,7 +5225,6 @@
     @GuardedBy("this")
     public void noteLongPartialWakelockStart(String name, String historyName, int uid,
             long elapsedRealtimeMs, long uptimeMs) {
-        uid = mapUid(uid);
         noteLongPartialWakeLockStartInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
     }
 
@@ -5260,15 +5259,21 @@
     @GuardedBy("this")
     private void noteLongPartialWakeLockStartInternal(String name, String historyName, int uid,
             long elapsedRealtimeMs, long uptimeMs) {
+        final int mappedUid = mapUid(uid);
         if (historyName == null) {
             historyName = name;
         }
-        if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName, uid,
-                0)) {
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_START, historyName,
+                mappedUid, 0)) {
             return;
         }
         addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
-                historyName, uid);
+                historyName, mappedUid);
+        if (mappedUid != uid) {
+            // Prevent the isolated uid mapping from being removed while the wakelock is
+            // being held.
+            incrementIsolatedUidRefCount(uid);
+        }
     }
 
     @GuardedBy("this")
@@ -5280,7 +5285,6 @@
     @GuardedBy("this")
     public void noteLongPartialWakelockFinish(String name, String historyName, int uid,
             long elapsedRealtimeMs, long uptimeMs) {
-        uid = mapUid(uid);
         noteLongPartialWakeLockFinishInternal(name, historyName, uid, elapsedRealtimeMs, uptimeMs);
     }
 
@@ -5315,15 +5319,20 @@
     @GuardedBy("this")
     private void noteLongPartialWakeLockFinishInternal(String name, String historyName, int uid,
             long elapsedRealtimeMs, long uptimeMs) {
+        final int mappedUid = mapUid(uid);
         if (historyName == null) {
             historyName = name;
         }
-        if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName, uid,
-                0)) {
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH, historyName,
+                mappedUid, 0)) {
             return;
         }
         addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
-                historyName, uid);
+                historyName, mappedUid);
+        if (mappedUid != uid) {
+            // Decrement the ref count for the isolated uid and delete the mapping if uneeded.
+            maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+        }
     }
 
     @GuardedBy("this")
diff --git a/apex/media/aidl/stable/android/media/Session2Token.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
similarity index 72%
copy from apex/media/aidl/stable/android/media/Session2Token.aidl
copy to core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
index c5980e9..ee50219 100644
--- a/apex/media/aidl/stable/android/media/Session2Token.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 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,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.media;
+package com.android.internal.policy;
 
-parcelable Session2Token;
+oneway interface IKeyguardLockedStateListener {
+    void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index b32a6b0..1eb446e 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -249,7 +249,7 @@
         dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED,
                 status.isPowerTransferLimited());
         dump.write("usb_power_brick_status", UsbPortStatusProto.USB_POWER_BRICK_STATUS,
-                UsbPort.powerBrickStatusToString(status.getPowerBrickStatus()));
+                UsbPort.powerBrickConnectionStatusToString(status.getPowerBrickConnectionStatus()));
         dump.end(token);
     }
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9bdcddf..1fd0410 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -888,6 +888,15 @@
         }
     }
 
+    /**
+     * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code
+     * null}, otherwise returns {@code null}.
+     */
+    @Nullable
+    public static <T> T getOrNull(@Nullable T[] items, int i) {
+        return (items != null && items.length > i) ? items[i] : null;
+    }
+
     public static @Nullable <T> T firstOrNull(T[] items) {
         return items.length > 0 ? items[0] : null;
     }
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
index c758504..f3af528 100644
--- a/core/java/com/android/internal/util/PerfettoTrigger.java
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -18,6 +18,7 @@
 
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.SparseLongArray;
 
 import java.io.IOException;
 
@@ -28,8 +29,9 @@
 public class PerfettoTrigger {
     private static final String TAG = "PerfettoTrigger";
     private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-    private static final long THROTTLE_MILLIS = 60000;
-    private static volatile long sLastTriggerTime = -THROTTLE_MILLIS;
+    private static final long THROTTLE_MILLIS = 300000;
+    private static final SparseLongArray sLastInvocationPerTrigger = new SparseLongArray(100);
+    private static final Object sLock = new Object();
 
     /**
      * @param triggerName The name of the trigger. Must match the value defined in the AOT
@@ -38,18 +40,23 @@
     public static void trigger(String triggerName) {
         // Trace triggering has a non-negligible cost (fork+exec).
         // To mitigate potential excessive triggering by the API client we ignore calls that happen
-        // too quickl after the most recent trigger.
-        long sinceLastTrigger = SystemClock.elapsedRealtime() - sLastTriggerTime;
-        if (sinceLastTrigger < THROTTLE_MILLIS) {
-            Log.v(TAG, "Not triggering " + triggerName + " - not enough time since last trigger");
-            return;
+        // too quickly after the most recent trigger.
+        synchronized (sLock) {
+            long lastTrigger = sLastInvocationPerTrigger.get(triggerName.hashCode());
+            long sinceLastTrigger = SystemClock.elapsedRealtime() - lastTrigger;
+            if (sinceLastTrigger < THROTTLE_MILLIS) {
+                Log.v(TAG, "Not triggering " + triggerName
+                        + " - not enough time since last trigger");
+                return;
+            }
+
+            sLastInvocationPerTrigger.put(triggerName.hashCode(), SystemClock.elapsedRealtime());
         }
 
         try {
             ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
             Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
             pb.start();
-            sLastTriggerTime = SystemClock.elapsedRealtime();
         } catch (IOException e) {
             Log.w(TAG, "Failed to trigger " + triggerName, e);
         }
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index 17b84ff..d9c1e57 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -46,11 +46,21 @@
      * Converts a given drawable to a bitmap.
      */
     public static Bitmap convertToBitmap(Drawable icon) {
+        return convertToBitmapAtSize(icon, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+    }
+
+    /**
+     * Converts a given drawable to a bitmap, with width and height equal to the default icon size.
+     */
+    public static Bitmap convertToBitmapAtUserIconSize(Resources res, Drawable icon) {
+        int size = res.getDimensionPixelSize(R.dimen.user_icon_size);
+        return convertToBitmapAtSize(icon, size, size);
+    }
+
+    private static Bitmap convertToBitmapAtSize(Drawable icon, int width, int height) {
         if (icon == null) {
             return null;
         }
-        final int width = icon.getIntrinsicWidth();
-        final int height = icon.getIntrinsicHeight();
         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(bitmap);
         icon.setBounds(0, 0, width, height);
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index cfb2bf9..869da1f 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -20,15 +20,14 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
-import android.graphics.Point;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.Display;
 import android.view.IWindowManager;
 import android.view.Surface;
 import android.view.WindowManagerGlobal;
@@ -73,19 +72,16 @@
      * otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable.
      */
     public static int getRotationLockOrientation(Context context) {
-        if (!areAllRotationsAllowed(context)) {
-            final Point size = new Point();
-            final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-            try {
-                final int displayId = context.getDisplayId();
-                wm.getInitialDisplaySize(displayId, size);
-                return size.x < size.y ?
-                        Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Unable to get the display size");
-            }
+        if (areAllRotationsAllowed(context)) {
+            return Configuration.ORIENTATION_UNDEFINED;
         }
-        return Configuration.ORIENTATION_UNDEFINED;
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final int rotation =
+                context.getResources().getConfiguration().windowConfiguration.getRotation();
+        final boolean rotated = rotation % 2 != 0;
+        final int w = rotated ? metrics.heightPixels : metrics.widthPixels;
+        final int h = rotated ? metrics.widthPixels : metrics.heightPixels;
+        return w < h ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 78bb53d..5fa4a65 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -150,6 +150,7 @@
     private Icon mShortcutIcon;
     private View mAppNameDivider;
     private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this);
+    private ArrayList<MessagingGroup> mToRecycle = new ArrayList<>();
 
     public ConversationLayout(@NonNull Context context) {
         super(context);
@@ -472,6 +473,12 @@
         updateTitleAndNamesDisplay();
 
         updateConversationLayout();
+
+        // Recycle everything at the end of the update, now that we know it's no longer needed.
+        for (MessagingGroup group : mToRecycle) {
+            group.recycle();
+        }
+        mToRecycle.clear();
     }
 
     /**
@@ -745,18 +752,18 @@
             MessagingGroup group = oldGroups.get(i);
             if (!mGroups.contains(group)) {
                 List<MessagingMessage> messages = group.getMessages();
-                Runnable endRunnable = () -> {
-                    mMessagingLinearLayout.removeTransientView(group);
-                    group.recycle();
-                };
-
                 boolean wasShown = group.isShown();
                 mMessagingLinearLayout.removeView(group);
                 if (wasShown && !MessagingLinearLayout.isGone(group)) {
                     mMessagingLinearLayout.addTransientView(group, 0);
-                    group.removeGroupAnimated(endRunnable);
+                    group.removeGroupAnimated(() -> {
+                        mMessagingLinearLayout.removeTransientView(group);
+                        group.recycle();
+                    });
                 } else {
-                    endRunnable.run();
+                    // Defer recycling until after the update is done, since we may still need the
+                    // old group around to perform other updates.
+                    mToRecycle.add(group);
                 }
                 mMessages.removeAll(messages);
                 mHistoricMessages.removeAll(messages);
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 80d8bd7..8c61a12 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -1475,6 +1475,7 @@
         contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
+        contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
         contentContainer.setClipToOutline(true);
         return contentContainer;
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 47cb754..4aa00f6 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -64,7 +64,6 @@
         "libbase",
         "libcutils",
         "libharfbuzz_ng",
-        "libhwui",
         "liblog",
         "libminikin",
         "libz",
@@ -266,6 +265,7 @@
                 "libui",
                 "libgraphicsenv",
                 "libgui",
+                "libhwui",
                 "libmediandk",
                 "libpermission",
                 "libsensor",
@@ -344,9 +344,21 @@
             ],
             static_libs: [
                 "libandroidfw",
-                "libcompiler_rt",
-                "libutils",
+                "libbinary_parse",
+                "libdng_sdk",
+                "libft2",
                 "libhostgraphics",
+                "libhwui",
+                "libimage_type_recognition",
+                "libjpeg",
+                "libpiex",
+                "libpng",
+                "libtiff_directory",
+                "libui-types",
+                "libutils",
+                "libwebp-decode",
+                "libwebp-encode",
+                "libwuffs_mirror_release_c",
             ],
         },
         linux_glibc: {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 3e513df..93ba23b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -14,15 +14,31 @@
  * limitations under the License.
  */
 
-#include "jni.h"
-#include "core_jni_helpers.h"
-
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/graphics/jni_runtime.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
 #include <unicode/putil.h>
+#include <unicode/udata.h>
+
 #include <clocale>
 #include <sstream>
 #include <unordered_map>
 #include <vector>
 
+#include "core_jni_helpers.h"
+#include "jni.h"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#endif
+
+#include <iostream>
+
 using namespace std;
 
 /*
@@ -33,6 +49,33 @@
  */
 
 static JavaVM* javaVM;
+static jclass bridge;
+static jclass layoutLog;
+static jmethodID getLogId;
+static jmethodID logMethodId;
+
+extern int register_android_os_Binder(JNIEnv* env);
+extern int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env);
+
+typedef void (*FreeFunction)(void*);
+
+static void NativeAllocationRegistry_Delegate_nativeApplyFreeFunction(JNIEnv*, jclass,
+                                                                      jlong freeFunction,
+                                                                      jlong ptr) {
+    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+    FreeFunction nativeFreeFunction =
+            reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+    nativeFreeFunction(nativePtr);
+}
+
+static JNINativeMethod gMethods[] = {
+        NATIVE_METHOD(NativeAllocationRegistry_Delegate, nativeApplyFreeFunction, "(JJ)V"),
+};
+
+int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry_Delegate", gMethods,
+                                    NELEM(gMethods));
+}
 
 namespace android {
 
@@ -47,6 +90,7 @@
 extern int register_android_database_SQLiteDebug(JNIEnv* env);
 extern int register_android_os_FileObserver(JNIEnv* env);
 extern int register_android_os_MessageQueue(JNIEnv* env);
+extern int register_android_os_Parcel(JNIEnv* env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_SystemProperties(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
@@ -54,6 +98,11 @@
 extern int register_android_util_EventLog(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
+extern int register_android_view_KeyCharacterMap(JNIEnv* env);
+extern int register_android_view_KeyEvent(JNIEnv* env);
+extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+extern int register_android_view_VelocityTracker(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
 #define REG_JNI(name)      { name }
@@ -78,8 +127,10 @@
         {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
         {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
 #ifdef __linux__
+        {"android.os.Binder", REG_JNI(register_android_os_Binder)},
         {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
         {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+        {"android.os.Parcel", REG_JNI(register_android_os_Parcel)},
 #endif
         {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
         {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
@@ -88,11 +139,15 @@
         {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
         {"android.util.Log", REG_JNI(register_android_util_Log)},
         {"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
+        {"android.view.KeyCharacterMap", REG_JNI(register_android_view_KeyCharacterMap)},
+        {"android.view.KeyEvent", REG_JNI(register_android_view_KeyEvent)},
+        {"android.view.MotionEvent", REG_JNI(register_android_view_MotionEvent)},
+        {"android.view.VelocityTracker", REG_JNI(register_android_view_VelocityTracker)},
         {"com.android.internal.util.VirtualRefBasePtr",
          REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+        {"libcore.util.NativeAllocationRegistry_Delegate",
+         REG_JNI(register_libcore_util_NativeAllocationRegistry_Delegate)},
 };
-// Vector to store the names of classes that need delegates of their native methods
-static vector<string> classesToDelegate;
 
 static int register_jni_procs(const std::unordered_map<std::string, RegJNIRec>& jniRegMap,
         const vector<string>& classesToRegister, JNIEnv* env) {
@@ -102,36 +157,17 @@
             return -1;
         }
     }
+
+    if (register_android_graphics_classes(env) < 0) {
+        return -1;
+    }
+
     return 0;
 }
 
 int AndroidRuntime::registerNativeMethods(JNIEnv* env,
         const char* className, const JNINativeMethod* gMethods, int numMethods) {
-    string classNameString = string(className);
-    if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
-            != classesToDelegate.end()) {
-        // Register native methods to the delegate class <classNameString>_NativeDelegate
-        // by adding _Original to the name of each method.
-        replace(classNameString.begin(), classNameString.end(), '$', '_');
-        string delegateClassName = classNameString + "_NativeDelegate";
-        jclass clazz = env->FindClass(delegateClassName.c_str());
-        JNINativeMethod gTypefaceDelegateMethods[numMethods];
-        for (int i = 0; i < numMethods; i++) {
-            JNINativeMethod gTypefaceMethod = gMethods[i];
-            string newName = string(gTypefaceMethod.name) + "_Original";
-            gTypefaceDelegateMethods[i].name = strdup(newName.c_str());
-            gTypefaceDelegateMethods[i].signature = gTypefaceMethod.signature;
-            gTypefaceDelegateMethods[i].fnPtr = gTypefaceMethod.fnPtr;
-        }
-        int result = env->RegisterNatives(clazz, gTypefaceDelegateMethods, numMethods);
-        for (int i = 0; i < numMethods; i++) {
-            free((char*)gTypefaceDelegateMethods[i].name);
-        }
-        return result;
-    }
-
-    jclass clazz = env->FindClass(className);
-    return env->RegisterNatives(clazz, gMethods, numMethods);
+    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
 }
 
 JNIEnv* AndroidRuntime::getJNIEnv() {
@@ -164,6 +200,125 @@
     return result;
 }
 
+void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
+                     unsigned int line, const char* message) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint logPrio = severity;
+    jstring tagString = env->NewStringUTF(tag);
+    jstring messageString = env->NewStringUTF(message);
+
+    jobject bridgeLog = env->CallStaticObjectMethod(bridge, getLogId);
+
+    env->CallVoidMethod(bridgeLog, logMethodId, logPrio, tagString, messageString);
+
+    env->DeleteLocalRef(tagString);
+    env->DeleteLocalRef(messageString);
+    env->DeleteLocalRef(bridgeLog);
+}
+
+void LayoutlibAborter(const char* abort_message) {
+    // Layoutlib should not call abort() as it would terminate Studio.
+    // Throw an exception back to Java instead.
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jniThrowRuntimeException(env, "The Android framework has encountered a fatal error");
+}
+
+// This method has been copied/adapted from system/core/init/property_service.cpp
+// If the ro.product.cpu.abilist* properties have not been explicitly
+// set, derive them from ro.system.product.cpu.abilist* properties.
+static void property_initialize_ro_cpu_abilist() {
+    const std::string EMPTY = "";
+    const char* kAbilistProp = "ro.product.cpu.abilist";
+    const char* kAbilist32Prop = "ro.product.cpu.abilist32";
+    const char* kAbilist64Prop = "ro.product.cpu.abilist64";
+
+    // If the properties are defined explicitly, just use them.
+    if (base::GetProperty(kAbilistProp, EMPTY) != EMPTY) {
+        return;
+    }
+
+    std::string abilist32_prop_val;
+    std::string abilist64_prop_val;
+    const auto abilist32_prop = "ro.system.product.cpu.abilist32";
+    const auto abilist64_prop = "ro.system.product.cpu.abilist64";
+    abilist32_prop_val = base::GetProperty(abilist32_prop, EMPTY);
+    abilist64_prop_val = base::GetProperty(abilist64_prop, EMPTY);
+
+    // Merge ABI lists for ro.product.cpu.abilist
+    auto abilist_prop_val = abilist64_prop_val;
+    if (abilist32_prop_val != EMPTY) {
+        if (abilist_prop_val != EMPTY) {
+            abilist_prop_val += ",";
+        }
+        abilist_prop_val += abilist32_prop_val;
+    }
+
+    // Set these properties
+    const std::pair<const char*, const std::string&> set_prop_list[] = {
+            {kAbilistProp, abilist_prop_val},
+            {kAbilist32Prop, abilist32_prop_val},
+            {kAbilist64Prop, abilist64_prop_val},
+    };
+    for (const auto& [prop, prop_val] : set_prop_list) {
+        base::SetProperty(prop, prop_val);
+    }
+}
+
+static void* mmapFile(const char* dataFilePath) {
+#ifdef _WIN32
+    // Windows needs file path in wide chars to handle unicode file paths
+    int size = MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, NULL, 0);
+    std::vector<wchar_t> wideDataFilePath(size);
+    MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, wideDataFilePath.data(), size);
+    HANDLE file =
+            CreateFileW(wideDataFilePath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr);
+    if ((HANDLE)INVALID_HANDLE_VALUE == file) {
+        return nullptr;
+    }
+
+    struct CloseHandleWrapper {
+        void operator()(HANDLE h) { CloseHandle(h); }
+    };
+    std::unique_ptr<void, CloseHandleWrapper> mmapHandle(
+            CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
+    if (!mmapHandle) {
+        return nullptr;
+    }
+    return MapViewOfFile(mmapHandle.get(), FILE_MAP_READ, 0, 0, 0);
+#else
+    int fd = open(dataFilePath, O_RDONLY);
+    if (fd == -1) {
+        return nullptr;
+    }
+
+    struct stat sb;
+    if (fstat(fd, &sb) == -1) {
+        close(fd);
+        return nullptr;
+    }
+
+    void* addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (addr == MAP_FAILED) {
+        close(fd);
+        return nullptr;
+    }
+
+    close(fd);
+    return addr;
+#endif
+}
+
+static bool init_icu(const char* dataPath) {
+    void* addr = mmapFile(dataPath);
+    UErrorCode err = U_ZERO_ERROR;
+    udata_setCommonData(addr, &err);
+    if (err != U_ZERO_ERROR) {
+        return false;
+    }
+    return true;
+}
+
 } // namespace android
 
 using namespace android;
@@ -175,37 +330,82 @@
         return JNI_ERR;
     }
 
+    init_android_graphics();
+
     // Configuration is stored as java System properties.
     // Get a reference to System.getProperty
     jclass system = FindClassOrDie(env, "java/lang/System");
     jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty",
                                                          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
 
-    // Get the names of classes that have to delegate their native methods
-    auto delegateNativesToNativesString =
-            (jstring) env->CallStaticObjectMethod(system,
-                    getPropertyMethod, env->NewStringUTF("delegate_natives_to_natives"),
-                    env->NewStringUTF(""));
-    classesToDelegate = parseCsv(env, delegateNativesToNativesString);
-
     // Get the names of classes that need to register their native methods
     auto nativesClassesJString =
-            (jstring) env->CallStaticObjectMethod(system,
-                                                  getPropertyMethod, env->NewStringUTF("native_classes"),
-                                                  env->NewStringUTF(""));
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF("core_native_classes"),
+                                                 env->NewStringUTF(""));
     vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
 
+    jstring registerProperty =
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF(
+                                                         "register_properties_during_load"),
+                                                 env->NewStringUTF(""));
+    const char* registerPropertyString = env->GetStringUTFChars(registerProperty, 0);
+    if (strcmp(registerPropertyString, "true") == 0) {
+        // Set the system properties first as they could be used in the static initialization of
+        // other classes
+        if (register_android_os_SystemProperties(env) < 0) {
+            return JNI_ERR;
+        }
+        classesToRegister.erase(find(classesToRegister.begin(), classesToRegister.end(),
+                                     "android.os.SystemProperties"));
+        bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+        bridge = MakeGlobalRefOrDie(env, bridge);
+        jmethodID setSystemPropertiesMethod =
+                GetStaticMethodIDOrDie(env, bridge, "setSystemProperties", "()V");
+        env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
+        property_initialize_ro_cpu_abilist();
+    }
+    env->ReleaseStringUTFChars(registerProperty, registerPropertyString);
+
     if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
         return JNI_ERR;
     }
 
     // Set the location of ICU data
-    auto stringPath = (jstring) env->CallStaticObjectMethod(system,
-        getPropertyMethod, env->NewStringUTF("icu.dir"),
-        env->NewStringUTF(""));
+    auto stringPath = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                           env->NewStringUTF("icu.data.path"),
+                                                           env->NewStringUTF(""));
     const char* path = env->GetStringUTFChars(stringPath, 0);
-    u_setDataDirectory(path);
+    bool icuInitialized = init_icu(path);
     env->ReleaseStringUTFChars(stringPath, path);
+    if (!icuInitialized) {
+        return JNI_ERR;
+    }
+
+    jstring useJniProperty =
+            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                 env->NewStringUTF("use_bridge_for_logging"),
+                                                 env->NewStringUTF(""));
+    const char* useJniString = env->GetStringUTFChars(useJniProperty, 0);
+    if (strcmp(useJniString, "true") == 0) {
+        layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
+        layoutLog = MakeGlobalRefOrDie(env, layoutLog);
+        logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
+                                       "(ILjava/lang/String;Ljava/lang/String;)V");
+        if (bridge == nullptr) {
+            bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+            bridge = MakeGlobalRefOrDie(env, bridge);
+        }
+        getLogId = GetStaticMethodIDOrDie(env, bridge, "getLog",
+                                          "()Lcom/android/ide/common/rendering/api/ILayoutLog;");
+        android::base::SetLogger(LayoutlibLogger);
+        android::base::SetAborter(LayoutlibAborter);
+    } else {
+        // initialize logging, so ANDROD_LOG_TAGS env variable is respected
+        android::base::InitLogging(nullptr, android::base::StderrLogger);
+    }
+    env->ReleaseStringUTFChars(useJniProperty, useJniString);
 
     // Use English locale for number format to ensure correct parsing of floats when using strtof
     setlocale(LC_NUMERIC, "en_US.UTF-8");
@@ -213,3 +413,9 @@
     return JNI_VERSION_1_6;
 }
 
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*) {
+    JNIEnv* env = nullptr;
+    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+    env->DeleteGlobalRef(bridge);
+    env->DeleteGlobalRef(layoutLog);
+}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index d7eeb5f..93ce377 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -130,10 +130,6 @@
 
     mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
 
-    mInfo.flags = Flags<WindowInfo::Flag>(
-            env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
-    mInfo.type = static_cast<WindowInfo::Type>(
-            env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
     mInfo.dispatchingTimeout = std::chrono::milliseconds(
             env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
     mInfo.frameLeft = env->GetIntField(obj,
@@ -159,14 +155,72 @@
         env->DeleteLocalRef(regionObj);
     }
 
-    mInfo.visible = env->GetBooleanField(obj,
-            gInputWindowHandleClassInfo.visible);
-    mInfo.focusable = env->GetBooleanField(obj, gInputWindowHandleClassInfo.focusable);
-    mInfo.hasWallpaper = env->GetBooleanField(obj,
-            gInputWindowHandleClassInfo.hasWallpaper);
-    mInfo.paused = env->GetBooleanField(obj,
-            gInputWindowHandleClassInfo.paused);
-    mInfo.trustedOverlay = env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay);
+    const auto flags = Flags<WindowInfo::Flag>(
+            env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
+    const auto type = static_cast<WindowInfo::Type>(
+            env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
+    mInfo.layoutParamsFlags = flags;
+    mInfo.layoutParamsType = type;
+
+    using InputConfig = gui::WindowInfo::InputConfig;
+    // Determine the value for each of the InputConfig flags. We rely on a switch statement and
+    // -Wswitch-enum to give us a build error if we forget to explicitly handle an InputConfig flag.
+    mInfo.inputConfig = InputConfig::NONE;
+    InputConfig enumerationStart = InputConfig::NONE;
+    switch (enumerationStart) {
+        case InputConfig::NONE:
+            FALLTHROUGH_INTENDED;
+        case InputConfig::NOT_VISIBLE:
+            if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.visible) == JNI_FALSE) {
+                mInfo.inputConfig |= InputConfig::NOT_VISIBLE;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::NOT_FOCUSABLE:
+            if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.focusable) == JNI_FALSE) {
+                mInfo.inputConfig |= InputConfig::NOT_FOCUSABLE;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::NOT_TOUCHABLE:
+            if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
+                mInfo.inputConfig |= InputConfig::NOT_TOUCHABLE;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::PREVENT_SPLITTING:
+            if (!flags.test(WindowInfo::Flag::SPLIT_TOUCH)) {
+                mInfo.inputConfig |= InputConfig::PREVENT_SPLITTING;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER:
+            if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.hasWallpaper) == JNI_TRUE) {
+                mInfo.inputConfig |= InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::IS_WALLPAPER:
+            if (type == WindowInfo::Type::WALLPAPER) {
+                mInfo.inputConfig |= InputConfig::IS_WALLPAPER;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::PAUSE_DISPATCHING:
+            if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.paused) == JNI_TRUE) {
+                mInfo.inputConfig |= InputConfig::PAUSE_DISPATCHING;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::TRUSTED_OVERLAY:
+            if (env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay) == JNI_TRUE) {
+                mInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::WATCH_OUTSIDE_TOUCH:
+            if (flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+                mInfo.inputConfig |= InputConfig::WATCH_OUTSIDE_TOUCH;
+            }
+            FALLTHROUGH_INTENDED;
+        case InputConfig::SLIPPERY:
+            if (flags.test(WindowInfo::Flag::SLIPPERY)) {
+                mInfo.inputConfig |= InputConfig::SLIPPERY;
+            }
+    }
+
     mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
     mInfo.ownerPid = env->GetIntField(obj,
@@ -274,9 +328,9 @@
     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.name,
                         env->NewStringUTF(windowInfo.name.data()));
     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsFlags,
-                     static_cast<uint32_t>(windowInfo.flags.get()));
+                     static_cast<uint32_t>(windowInfo.layoutParamsFlags.get()));
     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsType,
-                     static_cast<int32_t>(windowInfo.type));
+                     static_cast<int32_t>(windowInfo.layoutParamsType));
     env->SetLongField(inputWindowHandle, gInputWindowHandleClassInfo.dispatchingTimeoutMillis,
                       std::chrono::duration_cast<std::chrono::milliseconds>(
                               windowInfo.dispatchingTimeout)
@@ -305,15 +359,17 @@
     env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.touchableRegion,
                         regionObj.get());
 
+    using InputConfig = gui::WindowInfo::InputConfig;
     env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.visible,
-                         windowInfo.visible);
+                         !windowInfo.inputConfig.test(InputConfig::NOT_VISIBLE));
     env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.focusable,
-                         windowInfo.focusable);
+                         !windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
     env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.hasWallpaper,
-                         windowInfo.hasWallpaper);
-    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.paused, windowInfo.paused);
+                         windowInfo.inputConfig.test(InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.paused,
+                         windowInfo.inputConfig.test(InputConfig::PAUSE_DISPATCHING));
     env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.trustedOverlay,
-                         windowInfo.trustedOverlay);
+                         windowInfo.inputConfig.test(InputConfig::TRUSTED_OVERLAY));
     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
                      static_cast<int32_t>(windowInfo.touchOcclusionMode));
     env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid);
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index d91d526..19402f7 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -118,11 +118,11 @@
 
         ScopedLocalRef<jobjectArray>
                 frameTimelineObjs(env,
-                                  env->NewObjectArray(vsyncEventData.frameTimelines.size(),
+                                  env->NewObjectArray(VsyncEventData::kFrameTimelinesLength,
                                                       gDisplayEventReceiverClassInfo
                                                               .frameTimelineClassInfo.clazz,
                                                       /*initial element*/ NULL));
-        for (int i = 0; i < vsyncEventData.frameTimelines.size(); i++) {
+        for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
             VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
             ScopedLocalRef<jobject>
                     frameTimelineObj(env,
@@ -130,8 +130,8 @@
                                                             .frameTimelineClassInfo.clazz,
                                                     gDisplayEventReceiverClassInfo
                                                             .frameTimelineClassInfo.init,
-                                                    frameTimeline.id,
-                                                    frameTimeline.expectedPresentTime,
+                                                    frameTimeline.vsyncId,
+                                                    frameTimeline.expectedPresentationTime,
                                                     frameTimeline.deadlineTimestamp));
             env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
         }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 336161c..fb5b5ff 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -625,12 +625,16 @@
 }
 
 static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
-                            jobject bufferObject) {
+                            jobject bufferObject, jlong fencePtr) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
     sp<GraphicBuffer> graphicBuffer(GraphicBuffer::fromAHardwareBuffer(
             android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, bufferObject)));
-    transaction->setBuffer(ctrl, graphicBuffer);
+    std::optional<sp<Fence>> optFence = std::nullopt;
+    if (fencePtr != 0) {
+        optFence = sp<Fence>{reinterpret_cast<Fence*>(fencePtr)};
+    }
+    transaction->setBuffer(ctrl, graphicBuffer, optFence);
 }
 
 static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -2133,7 +2137,7 @@
             (void*)nativeGetDisplayedContentSample },
     {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
             (void*)nativeSetGeometry },
-    {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;)V",
+    {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;J)V",
             (void*)nativeSetBuffer },
     {"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
     {"nativeSetDataSpace", "(JJI)V",
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 248db76..0c05da5 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -45,7 +45,7 @@
 // WARNING: Knows a little about the wire protocol used to communicate with Zygote.
 // TODO: Fix error handling.
 
-constexpr size_t MAX_COMMAND_BYTES = 12200;
+constexpr size_t MAX_COMMAND_BYTES = 32768;
 constexpr size_t NICE_NAME_BYTES = 50;
 
 // A buffer optionally bundled with a file descriptor from which we can fill it.
@@ -273,8 +273,6 @@
   char mBuffer[MAX_COMMAND_BYTES];
 };
 
-static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
-
 static int buffersAllocd(0);
 
 // Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68c8143..b39df2c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,7 +1624,7 @@
                 android:permissionGroup="android.permission-group.UNDEFINED"
                 android:label="@string/permlab_postNotification"
                 android:description="@string/permdesc_postNotification"
-                android:protectionLevel="dangerous" />
+                android:protectionLevel="dangerous|instant" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <!-- ====================================================================== -->
@@ -4119,6 +4119,13 @@
     <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION"
                 android:protectionLevel="internal|preinstalled" />
 
+    <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
+         <p>Protection level: internal|role
+         <p>Intended for use by ROLE_ASSISTANT only.
+    -->
+    <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+                android:protectionLevel="internal|role"/>
+
     <!-- Must be required by a {@link android.service.autofill.AutofillService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
@@ -4718,10 +4725,10 @@
     <permission android:name="android.permission.READ_FRAME_BUFFER"
         android:protectionLevel="signature|recents" />
 
-      <!-- @SystemApi Allows an application to change the touch mode state.
+      <!-- Allows an application to change the touch mode state.
            Without this permission, an app can only change the touch mode
            if it currently has focus.
-         @hide -->
+           @hide -->
     <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE"
         android:protectionLevel="signature" />
 
@@ -5963,7 +5970,7 @@
     <!-- @SystemApi Allows an application to turn on / off quiet mode.
          @hide -->
     <permission android:name="android.permission.MODIFY_QUIET_MODE"
-                android:protectionLevel="signature|privileged|development" />
+                android:protectionLevel="signature|privileged|development|role" />
 
     <!-- Allows internal management of the camera framework
          @hide -->
@@ -6912,6 +6919,14 @@
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.server.appsearch.contactsindexer.ContactsIndexerMaintenanceService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
+        <service android:name="com.android.server.BinaryTransparencyService$UpdateMeasurementsJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
             android:exported="false">
             <intent-filter>
diff --git a/core/res/res/anim/popup_enter_material.xml b/core/res/res/anim/popup_enter_material.xml
index ef5b7c0..9f771b9 100644
--- a/core/res/res/anim/popup_enter_material.xml
+++ b/core/res/res/anim/popup_enter_material.xml
@@ -22,7 +22,7 @@
         android:interpolator="@interpolator/standard"
         android:duration="@android:integer/config_activityDefaultDur" />
     <translate
-        android:fromYDelta="20dp"
+        android:fromYDelta="@android:dimen/popup_enter_animation_from_y_delta"
         android:toYDelta="0"
         android:interpolator="@interpolator/standard"
         android:duration="@android:integer/config_activityDefaultDur" />
diff --git a/core/res/res/anim/popup_exit_material.xml b/core/res/res/anim/popup_exit_material.xml
index 1efa702..2b79ddf 100644
--- a/core/res/res/anim/popup_exit_material.xml
+++ b/core/res/res/anim/popup_exit_material.xml
@@ -23,7 +23,7 @@
         android:duration="@android:integer/config_activityShortDur" />
     <translate
         android:fromYDelta="0"
-        android:toYDelta="-10dp"
+        android:toYDelta="@android:dimen/popup_exit_animation_to_y_delta"
         android:interpolator="@interpolator/standard_accelerate"
         android:duration="@android:integer/config_activityShortDur" />
 </set>
diff --git a/core/res/res/layout/media_route_chooser_dialog.xml b/core/res/res/layout/media_route_chooser_dialog.xml
index cd1c74f..bf73f4b 100644
--- a/core/res/res/layout/media_route_chooser_dialog.xml
+++ b/core/res/res/layout/media_route_chooser_dialog.xml
@@ -28,20 +28,21 @@
 
     <!-- Content to show when list is empty. -->
     <LinearLayout android:id="@android:id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="64dp"
-              android:orientation="horizontal"
-              android:paddingLeft="16dp"
-              android:paddingRight="16dp"
-              android:visibility="gone">
-        <ProgressBar android:layout_width="wrap_content"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="center" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp"
+        android:visibility="gone">
+        <ProgressBar android:id="@+id/media_route_progress_bar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center" />
         <TextView android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="center"
-                  android:paddingStart="16dp"
-                  android:text="@string/media_route_chooser_searching" />
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:paddingStart="16dp"
+            android:text="@string/media_route_chooser_searching" />
     </LinearLayout>
 
     <!-- Settings button. -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 50f6a42c..181a26f 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Slaan oor"</string>
     <string name="no_matches" msgid="6472699895759164599">"Geen passings nie"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Vind op bladsy"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# passing}other{# van {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Vee tans gedeelde berging uit …"</string>
     <string name="share" msgid="4157615043345227321">"Deel"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> werk tans op die agtergrond en gebruik batterykrag. Tik om na te gaan."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> werk al vir \'n lang tyd op die agtergrond. Tik om na te gaan."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Gaan aktiewe programme na"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Kan nie toegang tot kamera van hierdie toestel af kry nie"</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 2e6f3e0..bf06d0e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ዝለል"</string>
     <string name="no_matches" msgid="6472699895759164599">"ምንም ተመሳሳይ የለም።"</string>
     <string name="find_on_page" msgid="5400537367077438198">"በገፅ ላይ አግኝ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# ተዛማጅ}one{# ከ{total}}other{# ከ{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ተከናውኗል"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"የተጋራ ማከማቻን በመደምሰስ ላይ…"</string>
     <string name="share" msgid="4157615043345227321">"አጋራ"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ከበስተጀርባ በማሄድ ላይ ነው እና ባትሪ እየጨረሰ ነው። ለመገምገም መታ ያድርጉ።"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ከበስተጀርባ ለረጅም ጊዜ በማሄድ ላይ ነው። ለመገምገም መታ ያድርጉ።"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ንቁ መተግበሪያዎችን ይፈትሹ"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ከዚህ መሣሪያ ሆኖ ካሜራን መድረስ አይቻልም"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index e2234e4..94a02a6 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -308,10 +308,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"الوصول إلى تقويمك"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"‏إرسال رسائل قصيرة SMS وعرضها"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"الملفات والمستندات"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"الوصول إلى الملفات والمستندات على جهازك"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"الموسيقى والملفات الصوتية الأخرى"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"الوصول إلى الملفات الصوتية على جهازك"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"الصور والفيديوهات"</string>
@@ -343,7 +341,7 @@
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"تنفيذ إيماءات"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"يمكن النقر والتمرير بسرعة والتصغير أو التكبير بإصبعين وتنفيذ إيماءات أخرى."</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"إيماءات بصمات الإصبع:"</string>
-    <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"يمكن أن تلتقط الإيماءات التي تم تنفيذها على جهاز استشعار بصمة الإصبع في الجهاز."</string>
+    <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"يمكن أن تلتقط الإيماءات من أداة استشعار بصمة الإصبع في الجهاز."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"أخذ لقطة شاشة"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"يمكن أخذ لقطة شاشة."</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"إيقاف شريط الحالة أو تعديله"</string>
@@ -592,12 +590,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"أدخِل قفل الشاشة للمتابعة"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"تم اكتشاف بصمة إصبع جزئية."</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"يُرجى تنظيف مستشعر بصمات الإصبع ثم إعادة المحاولة."</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"تنظيف المستشعر ثم إعادة المحاولة"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"اضغط بقوة على المستشعر."</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"تم تحريك الإصبع ببطء شديد. يُرجى إعادة المحاولة."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"يمكنك تجربة بصمة إصبع أخرى."</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"الصورة ساطعة للغاية."</string>
@@ -605,10 +600,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"غيِّر موضع إصبعك قليلاً في كل مرة."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"لم يتمّ التعرّف على البصمة."</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"اضغط بقوة على المستشعر."</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"تمّت مصادقة الوجه"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string>
@@ -1512,8 +1505,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"التخطي"</string>
     <string name="no_matches" msgid="6472699895759164599">"ليس هناك أي مطابقات"</string>
     <string name="find_on_page" msgid="5400537367077438198">"بحث في الصفحة"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{مطابقة واحدة}zero{# من إجمالي {total}}two{# من إجمالي {total}}few{# من إجمالي {total}}many{# من إجمالي {total}}other{# من إجمالي {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"تم"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"جارٍ محو بيانات مساحة التخزين المشتركة…"</string>
     <string name="share" msgid="4157615043345227321">"مشاركة"</string>
@@ -2267,6 +2259,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"يعمل تطبيق <xliff:g id="APP">%1$s</xliff:g> في الخلفية ويستنفد شحن البطارية. انقر لمراجعة الإعدادات."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"يعمل تطبيق <xliff:g id="APP">%1$s</xliff:g> في الخلفية لفترة طويلة. انقر لمراجعة الإعدادات."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"التحقّق من التطبيقات النشطة"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"لا يمكن الوصول إلى الكاميرا من هذا الجهاز."</string>
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 90f6fca..1a36fe5 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"এৰি যাওক"</string>
     <string name="no_matches" msgid="6472699895759164599">"কোনো মিল নাই"</string>
     <string name="find_on_page" msgid="5400537367077438198">"পৃষ্ঠাত বিচাৰক"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# টা মিল}one{{total}ৰ #}other{{total}ৰ #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"সম্পন্ন হ’ল"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"শ্বেয়াৰ কৰি থোৱা ষ্ট’ৰেজ মচি থকা হৈছে…"</string>
     <string name="share" msgid="4157615043345227321">"শ্বেয়াৰ কৰক"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> নেপথ্যত চলি আছে আৰু অত্যধিক বেটাৰী খৰচ কৰিছে। পৰ্যালোচনা কৰিবলৈ টিপক।"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> নেপথ্যত দীৰ্ঘ সময় ধৰি চলি আছে। পৰ্যালোচনা কৰিবলৈ টিপক।"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"সক্ৰিয় এপ্‌সমূহ পৰীক্ষা কৰক"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"এইটো ডিভাইচৰ পৰা কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string>
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 8c4f0b6..610ac94 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"təqvimə daxil olun"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"göndərin və SMS mesajlarına baxın"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fayllar &amp; sənədlər"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"cihazınızda fayllara və sənədlərə giriş"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musiqi və digər audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"cihazınızdakı audio fayllarına giriş"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Foto və videolar"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Davam etmək üçün ekran kilidinizi daxil edin"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Barmaq izinin bir hissəsi aşkarlanıb"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Barmaq izi sensorunu silib yenidən cəhd edin"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensoru silib yenidən cəhd edin"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensora basıb saxlayın"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Barmağınızı çox yavaş hərəkət etdirdiniz. Lütfən, yenidən cəhd edin."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başqa bir barmaq izini sınayın"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çox işıqlıdır"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Hər dəfə barmağınızın yerini bir az dəyişdirin"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Barmaq izi tanınmır"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Sensora basıb saxlayın"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Üz doğrulandı"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Keç"</string>
     <string name="no_matches" msgid="6472699895759164599">"Uyğunluq yoxdur"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Səhifədə tap"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# uyğunluq}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Hazırdır"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Paylaşılan yaddaş silinir…"</string>
     <string name="share" msgid="4157615043345227321">"Paylaşın"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> arxa fonda işləyir və enerjini tükədir. Nəzərdən keçirmək üçün toxunun."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> uzun müddət arxa fonda işləyir. Nəzərdən keçirmək üçün toxunun."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Aktiv tətbiqləri yoxlayın"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Bu cihazdan kameraya giriş mümkün deyil"</string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0c3b88c..32a563f 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -305,10 +305,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"pristupi kalendaru"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"šalje i pregleda SMS poruke"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fajlovi i dokumenti"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"pristupanje fajlovima i dokumentima na uređaju"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muzika i drugi audio sadržaj"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"pristup audio fajlovima na uređaju"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Slike i video snimci"</string>
@@ -589,12 +587,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Upotrebite zaključavanje ekrana da biste nastavili"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Otkriven je delimičan otisak prsta"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Obrišite senzor za otisak prsta i probajte ponovo"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Obrišite senzor i probajte ponovo"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Jako pritisnite senzor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Previše sporo ste pomerili prst. Probajte ponovo."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probajte sa drugim otiskom prsta"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Previše je svetlo"</string>
@@ -602,10 +597,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put lagano promenite položaj prsta"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Jako pritisnite senzor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string>
@@ -1509,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nema podudaranja"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Pronađi na stranici"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# podudaranje}one{# od {total}}few{# od {total}}other{# od {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Gotovo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Briše se deljeni memorijski prostor…"</string>
     <string name="share" msgid="4157615043345227321">"Deli"</string>
@@ -2096,7 +2088,7 @@
     <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Obaveštenja"</string>
     <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Brza podešavanja"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Dijalog napajanja"</string>
-    <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Zaključani ekran"</string>
+    <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Zaključavanje ekrana"</string>
     <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Snimak ekrana"</string>
     <string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Kuka za slušalice"</string>
     <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Prečica za pristupačnost na ekranu"</string>
@@ -2264,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta u pozadini i troši bateriju. Dodirnite da biste pregledali."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je predugo pokrenuta u pozadini. Dodirnite da biste pregledali."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Proverite aktivne aplikacije"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Ne možete da pristupite kameri sa ovog uređaja"</string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index cf8c2dd..23887dc 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"атрымліваць доступ да вашага календара"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"адпраўляць і праглядаць SMS-паведамленні"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файлы і дакументы"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"доступ да файлаў і дакументаў на вашай прыладзе"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музыка і іншае аўдыя"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"доступ да аўдыяфайлаў на вашай прыладзе"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Фота і відэа"</string>
@@ -549,7 +547,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дазволіць праграме адпраўляць рэкламу на прылады з Bluetooth, якія знаходзяцца паблізу"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"вызначаць адлегласць паміж прыладамі з звышшырокапалоснай сувяззю"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дазволіць праграме вызначаць адлегласць паміж прыладамі паблізу, якія выкарыстоўваюць звышшырокапалосную сувязь"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Узаемадзеянне з прыладамі з Wi‑Fi паблізу"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"узаемадзейнічаць з прыладамі з Wi‑Fi паблізу"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Праграма зможа адпраўляць даныя на прылады Wi-Fi паблізу, падключацца да іх і вызначаць іх месцазнаходжанне"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Інфармацыя пра прыярытэтны сэрвіс аплаты NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дазваляе праграме атрымаць доступ да інфармацыі пра прыярытэтны сэрвіс аплаты NFC, напрыклад зарэгістраваныя ідэнтыфікатары праграм і маршруты адпраўкі даных."</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Каб працягнуць, скарыстайце свой сродак блакіроўкі экрана"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Адбітак пальца адсканіраваны не цалкам"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Ачысціце сканер адбіткаў пальцаў і паўтарыце спробу"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Ачысціце сканер і паўтарыце спробу"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Моцна націсніце на сканер"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Палец рухаўся занадта павольна. Паспрабуйце яшчэ раз."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Паспрабуйце іншы адбітак пальца"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Занадта светла"</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Кожны раз крыху мяняйце пазіцыю пальца"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Адбітак пальца не распазнаны"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Моцна націсніце на сканер"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Твар распазнаны"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Прапусціць"</string>
     <string name="no_matches" msgid="6472699895759164599">"Няма супадзенняў"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Знайсці на старонцы"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# супадзенне}one{# з {total}}few{# з {total}}many{# з {total}}other{# з {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Гатова"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Сціраюцца даныя абагуленага сховішча…"</string>
     <string name="share" msgid="4157615043345227321">"Абагуліць"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> працуе ў фонавым рэжыме і расходуе зарад акумулятара. Націсніце, каб праглядзець."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> працуе ў фонавым рэжыме працяглы час. Націсніце, каб праглядзець."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Праверце актыўныя праграмы"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"З гэтай прылады няма доступу да камеры."</string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 7183dce..307df89 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"има достъп до календара ви"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"да изпраща и преглежда SMS съобщения"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файлове и документи"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"достъп до файловете и документите на устройството ви"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музика и друго аудиосъдържание"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"да има достъп до аудиофайловете на устройството ви"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Снимки и видеоклипове"</string>
@@ -547,8 +545,8 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Разрешава на приложението да рекламира на устройства с Bluetooth в близост"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"опред. на относителната позиция м/у у-вата с ултрашироколентови сигнали в близост"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Разрешаване на приложението да определя относителната позиция между устройствата с ултрашироколентови сигнали в близост"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Взаимодействие с устройствата с Wi-Fi в близост"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Разрешава на приложението да рекламира, да се свързва и да определя относителната позиция на у-вата с Wi-Fi в близост"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаимодействие с устройствата с Wi-Fi в близост"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Разрешава на приложението да рекламира, да се свързва и да определя относителната позиция на устройствата с Wi-Fi в близост"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информация за предпочитаната услуга за плащане чрез NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дава възможност на приложението да получава информация за предпочитаната услуга за плащане чрез NFC, като например регистрирани помощни средства и местоназначение."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"контролиране на комуникацията в близкото поле"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Въведете опцията си за заключване на екрана, за да продължите"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Установен е частичен отпечатък"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатъкът не бе обработен. Моля, опитайте отново."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Почистете сензора за отпечатъци и опитайте отново"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Почистете сензора и опитайте отново"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Натиснете добре върху сензора"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Опитайте с друг отпечатък"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Твърде светло е"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Всеки път променяйте леко позицията на пръста си"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечатъкът не е разпознат"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Натиснете добре върху сензора"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е удостоверено"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Пропускане"</string>
     <string name="no_matches" msgid="6472699895759164599">"Няма съответствия"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Намиране в страницата"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# съответствие}other{# от {total} съответствия}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Споделеното хранилище се изтрива…"</string>
     <string name="share" msgid="4157615043345227321">"Споделяне"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> работи на заден план и изразходва батерията. Докоснете за преглед."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> работи на заден план от дълго време. Докоснете за преглед."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверете активните приложения"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Не може да се осъществи достъп до камерата от това устройство"</string>
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 82932dc..bb5c1022 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"এড়িয়ে যান"</string>
     <string name="no_matches" msgid="6472699895759164599">"কোনো মিল নেই"</string>
     <string name="find_on_page" msgid="5400537367077438198">"পৃষ্ঠায় খুঁজুন"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{#টি ম্যাচ}one{{total}টির মধ্যে #টি}other{{total}টির মধ্যে #টি}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"সম্পন্ন হয়েছে"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"শেয়ার করা স্টোরেজ মুছে ফেলা হচ্ছে…"</string>
     <string name="share" msgid="4157615043345227321">"শেয়ার করুন"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ব্যাকগ্রাউন্ডে চলছে এবং এর ফলে ব্যাটারির চার্জ কমে যাচ্ছে। পর্যালোচনা করতে ট্যাপ করুন।"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> অনেকক্ষণ ধরে ব্যাকগ্রাউন্ডে চলছে। পর্যালোচনা করতে ট্যাপ করুন।"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"অ্যাক্টিভ অ্যাপ চেক করুন"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"এই ডিভাইস থেকে ক্যামেরা অ্যাক্সেস করা যাচ্ছে না"</string>
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 4f4f7d6..b53f34c 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1502,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nema podudaranja"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Pronađi na stranici"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# podudaranje}one{# od ukupno {total}}few{# od ukupno {total}}other{# od ukupno {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Gotovo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Brisanje dijeljene pohrane…"</string>
     <string name="share" msgid="4157615043345227321">"Dijeli"</string>
@@ -2257,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta u pozadini i troši bateriju. Dodirnite da pregledate."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> dugo radi u pozadini. Dodirnite da pregledate."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Provjerite aktivne aplikacije"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nije moguće pristupiti kameri s ovog uređaja"</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fa531ad..b124335 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"accedir al calendari"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"enviar i llegir missatges SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fitxers i documents"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"accedir als fitxers i documents del dispositiu"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música i altres fitxers d\'àudio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"accedir a fitxers d\'àudio del dispositiu"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos i vídeos"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introdueix el teu bloqueig de pantalla per continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"S\'ha detectat una empremta digital parcial"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Neteja el sensor d\'empremtes digitals i torna-ho a provar"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Neteja el sensor i torna-ho a provar"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prem el sensor de manera ferma"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prova una altra empremta digital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hi ha massa llum"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Canvia lleugerament la posició del dit en cada intent"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"L\'empremta digital no s\'ha reconegut"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Prem el sensor de manera ferma"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Omet"</string>
     <string name="no_matches" msgid="6472699895759164599">"No s\'ha trobat cap coincidència"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Troba-ho a la pàgina"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidència}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Fet"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"S\'està esborrant l\'emmagatzematge compartit…"</string>
     <string name="share" msgid="4157615043345227321">"Comparteix"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> s\'està executant en segon pla i consumeix bateria. Toca per revisar-ho."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Fa molta estona que <xliff:g id="APP">%1$s</xliff:g> s\'està executant en segon pla. Toca per revisar-ho."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Consulta les aplicacions actives"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"No es pot accedir a la càmera des d\'aquest dispositiu"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ee9d7bc..e7c3662 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"přístup ke kalendáři"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"odesílání a zobrazování zpráv SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Soubory a dokumenty"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"přístup k souborům a dokumentům v zařízení"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Hudba a ostatní zvuk"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"přístup ke zvukovým souborům v zařízení"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotky a videa"</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Pokračujte zadáním zámku obrazovky"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Byla zjištěna jen část otisku prstu"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Vyčistěte snímač otisků prstů a zkuste to znovu"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Vyčistěte senzor a zkuste to znovu"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pevně zatlačte na senzor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Zkuste jiný otisk prstu"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Je příliš světlo"</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Pokaždé lehce změňte polohu prstu"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisk prstu nebyl rozpoznán"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Pevně zatlačte na senzor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Obličej byl ověřen"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Přeskočit"</string>
     <string name="no_matches" msgid="6472699895759164599">"Žádné shody"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Hledat na stránce"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# shoda}few{# z {total}}many{# z {total}}other{# z {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Hotovo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Mazání sdíleného úložiště…"</string>
     <string name="share" msgid="4157615043345227321">"Sdílet"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikace <xliff:g id="APP">%1$s</xliff:g> je spuštěna na pozadí a vybíjí baterii. Klepnutím ji zkontrolujete."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikace <xliff:g id="APP">%1$s</xliff:g> je už dlouhou dobu spuštěna na pozadí. Klepnutím ji zkontrolujete."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Zkontrolujte aktivní aplikace"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Z tohoto zařízení nelze získat přístup k fotoaparátu"</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 66a31e7..94d97b0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"have adgang til din kalender"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Sms"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"sende og se sms-beskeder"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Filer og dokumenter"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"få adgang til filer og dokumenter på din enhed"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musik og anden lyd"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"Få adgang til lydfiler på din enhed"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Billeder og videoer"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"fastlægge den relative position mellem UWB-enheder i nærheden"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillad, at appen fastlægger den relative position mellem UWB-enheder (Ultra-Wideband) i nærheden"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagere med Wi‑Fi-enheder i nærheden"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Giver appen tilladelse til at informere om, oprette forbindelse til og fastslå den relative placering af Wi‑Fi -enheder i nærheden"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Giver appen tilladelse til at informere om, oprette forbindelse til og fastslå den relative placering af Wi‑Fi-enheder i nærheden"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Foretrukne oplysninger vedrørende NFC-betalingstjeneste"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillader, at appen får foretrukne oplysninger vedrørende NFC-betalingstjeneste, f.eks. registrerede hjælpemidler og rutedestinationer."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"administrere Near Field Communication"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Angiv din skærmlås for at fortsætte"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Et delvist fingeraftryk blev registreret"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengør fingeraftrykslæseren, og prøv igen"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengør læseren, og prøv igen"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Hold fingeren nede på læseren"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du bevægede fingeren for langsomt. Prøv igen."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv med et andet fingeraftryk"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Der er for lyst"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flyt fingeren en smule hver gang"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingeraftrykket blev ikke genkendt"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Hold fingeren nede på læseren"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansigtet er godkendt"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Spring over"</string>
     <string name="no_matches" msgid="6472699895759164599">"Der er ingen matches"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Find på siden"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# resultat}one{# af {total}}other{# af {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Udfør"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Sletter delt lagerplads…"</string>
     <string name="share" msgid="4157615043345227321">"Del"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> kører i baggrunden og dræner batteriet. Tryk for at gennemgå."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> har kørt i baggrunden i lang tid. Tryk for at gennemgå."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Tjek aktive apps"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Kameraet kan ikke tilgås fra denne enhed"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 781dd99..aa0e3ed 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"auf deinen Kalender zugreifen"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS senden und abrufen"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Dateien und Dokumente"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"Auf Dateien und Dokumente auf deinem Gerät zugreifen"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musik &amp; andere Audiodateien"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"Zugriff auf Audiodateien auf deinem Gerät"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos &amp; Videos"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Relative Distanz zwischen Ultrabreitband-Geräten in der Nähe bestimmen"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ermöglicht der App, die relative Distanz zwischen Ultrabreitband-Geräten in der Nähe zu bestimmen"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mit WLAN-Geräten in der Nähe interagieren"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Erlaubt der App, Inhalte an WLAN-Geräte in der Nähe zu senden, sich mit ihnen zu verbinden und ihre relative Positionierung zu ermitteln"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Erlaubt der App, Inhalte an WLAN-Geräte in der Nähe zu senden, sich mit ihnen zu verbinden und ihre relative Position zu ermitteln"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informationen zum bevorzugten NFC-Zahlungsdienst"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ermöglicht der App, Informationen zum bevorzugten NFC-Zahlungsdienst abzurufen, etwa registrierte Hilfsmittel oder das Routenziel."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Nahfeldkommunikation steuern"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Displaysperre eingeben, um fortzufahren"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Fingerabdruck wurde nur teilweise erkannt"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Reinige den Fingerabdrucksensor und versuch es noch einmal"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Reinige den Sensor und versuche es noch einmal"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Drücke fest auf den Sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger zu langsam bewegt. Bitte versuche es noch einmal."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Anderen Fingerabdruck verwenden"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zu hell"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ändere jedes Mal die Position deines Fingers"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingerabdruck nicht erkannt"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Drücke fest auf den Sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesicht authentifiziert"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Überspringen"</string>
     <string name="no_matches" msgid="6472699895759164599">"Keine Treffer"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Auf Seite suchen"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# Übereinstimmung}other{# von {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Fertig"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Freigegebener Speicher wird gelöscht…"</string>
     <string name="share" msgid="4157615043345227321">"Teilen"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> wird im Hintergrund ausgeführt und belastet den Akku. Zum Prüfen tippen."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> wird schon längere Zeit im Hintergrund ausgeführt. Zum Prüfen tippen."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Aktive Apps prüfen"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Von diesem Gerät aus ist kein Zugriff auf die Kamera möglich"</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c9716d6..41dc664 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"έχει πρόσβαση στο ημερολόγιό σας"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"στέλνει και να διαβάζει μηνύματα SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Αρχεία και έγγραφα"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"πρόσβαση σε αρχεία και έγγραφα στη συσκευή σας"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Μουσική και άλλο ηχητικό περιεχόμενο"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"πρόσβαση σε αρχεία ήχου στη συσκευή σας"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Φωτογραφίες και βίντεο"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Χρησιμοποιήστε το κλείδωμα οθόνης για να συνεχίσετε"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Εντοπίστηκε μέρους του δακτυλικού αποτυπώματος"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Δεν ήταν δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Καθαρίστε τον αισθητήρα δακτυλικών αποτυπωμάτων και δοκιμάστε ξανά"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Καθαρίστε τον αισθητήρα και δοκιμάστε ξανά"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Πιέστε σταθερά τον αισθητήρα"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Δοκιμάστε άλλο δακτυλικό αποτύπωμα"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Υπερβολικά έντονος φωτισμός"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Αλλάζετε ελαφρώς τη θέση του δακτύλου σας κάθε φορά."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Πιέστε σταθερά τον αισθητήρα"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Παράβλεψη"</string>
     <string name="no_matches" msgid="6472699895759164599">"Δεν υπάρχουν αποτελέσματα"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Εύρεση στη σελίδα"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# αποτέλεσμα}other{# από {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Τέλος"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Διαγραφή κοινόχρηστου αποθηκευτικού χώρου…"</string>
     <string name="share" msgid="4157615043345227321">"Κοινή χρ."</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται στο παρασκήνιο και καταναλώνει μπαταρία. Πατήστε για έλεγχο."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται στο παρασκήνιο για πολύ ώρα. Πατήστε για έλεγχο."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Έλεγχος ενεργών εφαρμογών"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Δεν είναι δυνατή η πρόσβαση στην κάμερα από αυτήν τη συσκευή."</string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 79a2e38..4ac7605 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
     <string name="no_matches" msgid="6472699895759164599">"No matches"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Find on page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# match}other{# of {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Done"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Erasing shared storage…"</string>
     <string name="share" msgid="4157615043345227321">"Share"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> is running in the background and draining battery. Tap to review."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> is running in the background for a long time. Tap to review."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Check active apps"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Cannot access camera from this device"</string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index da99fdd..5bd7816 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
     <string name="no_matches" msgid="6472699895759164599">"No matches"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Find on page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# match}other{# of {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Done"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Erasing shared storage…"</string>
     <string name="share" msgid="4157615043345227321">"Share"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> is running in the background and draining battery. Tap to review."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> is running in the background for a long time. Tap to review."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Check active apps"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Cannot access camera from this device"</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index f01b9af..39dd7d4 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
     <string name="no_matches" msgid="6472699895759164599">"No matches"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Find on page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# match}other{# of {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Done"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Erasing shared storage…"</string>
     <string name="share" msgid="4157615043345227321">"Share"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> is running in the background and draining battery. Tap to review."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> is running in the background for a long time. Tap to review."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Check active apps"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Cannot access camera from this device"</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e509c70..c18ddba 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
     <string name="no_matches" msgid="6472699895759164599">"No matches"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Find on page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# match}other{# of {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Done"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Erasing shared storage…"</string>
     <string name="share" msgid="4157615043345227321">"Share"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> is running in the background and draining battery. Tap to review."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> is running in the background for a long time. Tap to review."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Check active apps"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Cannot access camera from this device"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 23531ea..e2fb2f8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acceder al calendario"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"enviar y ver mensajes SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Archivos y documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"Accede a archivos y documentos en tu dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música y otro contenido de audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"acceder a los archivos de audio en tu dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos y videos"</string>
@@ -341,7 +339,7 @@
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos del sensor de huellas dactilares"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tomar captura de pantalla"</string>
-    <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar una captura de la pantalla."</string>
+    <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar capturas de pantalla."</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra de estado"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"aparecer en la barra de estado"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ingresa tu bloqueo de pantalla para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Detección parcial de una huella dactilar"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se pudo procesar la huella dactilar. Vuelve a intentarlo."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpia el sensor de huellas dactilares y vuelve a intentarlo"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpia el sensor y vuelve a intentarlo"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Presiona con firmeza el sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Moviste el dedo muy lento. Vuelve a intentarlo."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella dactilar"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia un poco la posición del dedo cada vez"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"No se reconoció la huella dactilar"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Presiona con firmeza el sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Se autenticó el rostro"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
     <string name="no_matches" msgid="6472699895759164599">"Sin coincidencias"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Buscar en la página"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Listo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamiento compartido…"</string>
     <string name="share" msgid="4157615043345227321">"Compartir"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> se está ejecutando en segundo plano y está agotando la batería. Presiona para revisar esta actividad."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Hace mucho tiempo que <xliff:g id="APP">%1$s</xliff:g> se está ejecutando en segundo plano. Presiona para revisar esta actividad."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Consulta las apps activas"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"No se puede acceder a la cámara desde este dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7c0d36b..a37d4e8 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acceder a tu calendario"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"enviar y ver mensajes SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Archivos y documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"acceder a archivos y documentos de tu dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música y otros archivos de audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"acceder a los archivos de audio de tu dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos y vídeos"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"calcular posición de dispositivos de banda ultraancha cercanos"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la aplicación determine la posición relativa de los dispositivos de banda ultraancha cercanos"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Interactuar con dispositivos Wi-Fi cercanos"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite a la aplicación mostrar información de dispositivos Wi-Fi cercanos, conectarse a ellos y determinar su posición"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite a la aplicación mostrar, conectar y determinar la posición relativa de dispositivos Wi-Fi cercanos"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre el servicio de pago por NFC preferido"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la aplicación obtenga información sobre el servicio de pago por NFC preferido, como identificadores de aplicación registrados y destinos de rutas."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicación de campo cercano (NFC)"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduce tu bloqueo de pantalla para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Huella digital parcial detectada"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpia el sensor de huellas digitales e inténtalo de nuevo"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpia el sensor e inténtalo de nuevo"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mantén pulsado firmemente el sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Has movido el dedo demasiado despacio. Vuelve a intentarlo."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella digital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia ligeramente el dedo de posición cada vez"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Huella digital no reconocida"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Mantén pulsado firmemente el sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Saltar"</string>
     <string name="no_matches" msgid="6472699895759164599">"No hay coincidencias."</string>
     <string name="find_on_page" msgid="5400537367077438198">"Buscar en la página"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Hecho"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamiento compartido…"</string>
     <string name="share" msgid="4157615043345227321">"Compartir"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> se está ejecutando en segundo plano y consumiendo batería. Toca para revisarlo."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> lleva mucho tiempo ejecutándose en segundo plano. Toca para revisarlo."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Consultar aplicaciones activas"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"No se puede acceder a la cámara desde este dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8c2e458..0539582 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"juurdepääs kalendrile"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"saata ja vaadata SMS-sõnumeid"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Failid ja dokumendid"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"juurdepääs teie seadmes olevatele failidele ja dokumentidele"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muusika ja muud helifailid"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"pääseda juurde teie seadmes olevatele helifailidele"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotod ja videod"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Jätkamiseks sisestage oma ekraanilukk"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Tuvastati osaline sõrmejälg"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Puhastage sõrmejäljeandur ja proovige uuesti"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Puhastage andur ja proovige uuesti"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Vajutage tugevalt andurile"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proovige teist sõrmejälge"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liiga ere"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Muutke iga kord pisut oma sõrme asendit"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sõrmejälge ei tuvastatud"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Vajutage tugevalt andurile"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Nägu on autenditud"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Jäta vahele"</string>
     <string name="no_matches" msgid="6472699895759164599">"Vasted puuduvad"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Otsige lehelt"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# vaste}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Valmis"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Jagatud salvestusruumi tühjendamine …"</string>
     <string name="share" msgid="4157615043345227321">"Jaga"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> töötab taustal ja kulutab akut. Puudutage ülevaatamiseks."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> on taustal töötanud kaua aega. Puudutage ülevaatamiseks."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Vaadake aktiivseid rakendusi"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Sellest seadmest ei pääse kaamerale juurde"</string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 4c07e40..d6c2711 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"atzitu egutegia"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMSak"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"bidali eta ikusi SMS mezuak"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fitxategiak eta dokumentuak"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"gailuko fitxategiak eta dokumentuak atzitu"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musika eta bestelako audioa"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"atzitu gailuko audio-fitxategiak"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Argazkiak eta bideoak"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Aurrera egiteko, desblokeatu pantailaren blokeoa"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Hatz-marka ez da osorik hauteman"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Garbitu hatz-marken sentsorea eta saiatu berriro"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Garbitu sentsorea eta saiatu berriro"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sakatu irmo sentsorea"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Mantsoegi mugitu duzu hatza. Saiatu berriro."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Erabili beste hatz-marka bat"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Argi gehiegi dago"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Aldi bakoitzean, aldatu hatzaren posizioa apur bat"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Ez da ezagutu hatz-marka"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Sakatu irmo sentsorea"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autentifikatu da aurpegia"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Saltatu"</string>
     <string name="no_matches" msgid="6472699895759164599">"Ez dago emaitzarik"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Aurkitu orri honetan"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# emaitza}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Eginda"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Biltegi partekatuko eduki guztia ezabatzen…"</string>
     <string name="share" msgid="4157615043345227321">"Partekatu"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> atzeko planoan exekutatzen eta bateria xahutzen ari da. Sakatu berrikusteko."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> aplikazioak denbora asko darama atzeko planoan exekutatzen. Sakatu berrikusteko."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Ikusi zer aplikazio dauden aktibo"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Ezin da atzitu kamera gailu honetatik"</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9b35e07..234fc56 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"دسترسی به تقویم شما"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"پیامک"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"ارسال و مشاهده پیامک‌ها"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"فایل‌ها و اسناد"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"دسترسی به فایل‌ها و اسناد موجود در دستگاه"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"موسیقی و فایل‌های صوتی دیگر"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"دسترسی به فایل‌های صوتی موجود در دستگاه"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"عکس‌ها و ویدیوها"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"برای ادامه، قفل صفحه‌تان را وارد کنید"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"بخشی از اثر انگشت شناسایی شد"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"حسگر اثر انگشت را تمیز و دوباره امتحان کنید"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"حسگر را تمیز و دوباره امتحان کنید"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"محکم روی حسگر فشار دهید"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"اثر انگشت دیگری را امتحان کنید"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"خیلی روشن است"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"هربار موقعیت انگشتتان را کمی تغییر دهید"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"اثر انگشت تشخیص داده نشد"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"محکم روی حسگر فشار دهید"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالت‌سنجی شد"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالت‌سنجی شد"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"رد شدن"</string>
     <string name="no_matches" msgid="6472699895759164599">"مورد منطبقی موجود نیست"</string>
     <string name="find_on_page" msgid="5400537367077438198">"یافتن در صفحه"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# مورد منطبق}one{# از {total}}other{# از {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"تمام"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"درحال پاک کردن فضای ذخیره‌سازی هم‌رسانی‌شده…"</string>
     <string name="share" msgid="4157615043345227321">"هم‌رسانی"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> در پس‌زمینه اجرا می‌شود و شارژ باتری را تخلیه می‌کند. برای مرور، ضربه بزنید."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> برای مدتی طولانی در پس‌زمینه اجرا می‌شود. برای مرور، ضربه بزنید."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"بررسی برنامه‌های فعال"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"نمی‌توان از این دستگاه به دوربین دسترسی پیدا کرد"</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 10adae8..a07d941 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"käyttää kalenteria"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Tekstiviestit"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"lähettää ja tarkastella tekstiviestejä"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Tiedostot ja dokumentit"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"pääsyn laitteesi tiedostoihin ja dokumentteihin"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musiikki ja muu audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"pääsy laitteesi audiotiedostoihin"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Kuvat ja videot"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Jatka lisäämällä näytön lukituksen avaustapa"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Osittainen sormenjälki havaittu"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sormenjäljen prosessointi epäonnistui. Yritä uudelleen."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Puhdista sormenjälkitunnistin ja yritä uudelleen"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Puhdista anturi ja yritä uudelleen"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Paina anturia voimakkaasti"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Kokeile toista sormenjälkeä"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liian kirkas"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Liikuta sormeasi hieman joka kerralla"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sormenjälkeä ei tunnistettu"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Paina anturia voimakkaasti"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Kasvot tunnistettu"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Ohita"</string>
     <string name="no_matches" msgid="6472699895759164599">"Ei tuloksia"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Etsi sivulta"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# osuma}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Valmis"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Tyhjennetään jaettua tallennustilaa…"</string>
     <string name="share" msgid="4157615043345227321">"Jaa"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> on käynnissä taustalla ja kuluttaa akkua. Tarkista napauttamalla."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> on ollut käynnissä taustalla pitkän aikaa. Tarkista napauttamalla."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Tarkista aktiiviset sovellukset"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Ei pääsyä kameraan tältä laitteelta"</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 43b061d..07af7ba 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"accéder à votre agenda"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Messagerie texte"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"envoyer et afficher des messages texte"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fichiers et documents"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"accédez aux fichiers et aux documents sur votre appareil"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musique et autres fichiers audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"accéder aux fichiers audio de votre appareil"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Photos et vidéos"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"déterminer la position relative entre des appareils à bande ultralarge à proximité"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autorisez l\'application à déterminer la position relative entre des appareils à bande ultralarge à proximité"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'application d\'annoncer, de se connecter et de déterminer la position relative des appareils Wi-Fi à proximité"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'application de diffuser des annonces, de se connecter et de déterminer la position relative des appareils Wi-Fi à proximité"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information sur le service préféré de paiement CCP"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'application d\'obtenir de l\'information sur le service préféré de paiement CCP comme les aides enregistrées et la route de destination."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"gérer la communication en champ proche"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Entrez votre verrouillage d\'écran pour continuer"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Empreinte digitale partielle détectée"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Nettoyez le capteur d\'empreintes digitales et réessayez"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Nettoyez le capteur et réessayez"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Appuyez fermement sur le capteur"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte digitale"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop lumineux"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Modifiez légèrement la position de votre doigt chaque fois"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Empreinte digitale non reconnue"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Appuyez fermement sur le capteur"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
     <string name="no_matches" msgid="6472699895759164599">"Aucune partie"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}other{# sur {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Terminé"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Effacement du stockage partagé en cours…"</string>
     <string name="share" msgid="4157615043345227321">"Partager"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> s\'exécute en arrière-plan et décharge rapidement la pile. Touchez pour examiner."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> s\'exécute en arrière-plan depuis longtemps. Touchez pour examiner."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Vérifier les applications actives"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Impossible d\'accéder à l\'appareil photo à partir de cet appareil"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7770aed..6e55c1d 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"accéder à votre agenda"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"envoyer et consulter des SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fichiers et documents"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"accéder aux fichiers et documents sur votre appareil"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musique et autres contenus audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"accès aux fichiers audio sur votre appareil"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Photos et vidéos"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Utilisez le verrouillage de l\'écran pour continuer"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Empreinte partielle détectée"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Nettoyez le lecteur d\'empreinte digitale et réessayez"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Nettoyez le lecteur et réessayez"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Appuyez bien sur le lecteur"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop de lumière"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Changez légèrement de position chaque fois"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Empreinte digitale non reconnue"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Appuyez bien sur le lecteur"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
     <string name="no_matches" msgid="6472699895759164599">"Aucune correspondance"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Rechercher sur la page"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondance}one{# sur {total}}other{# sur {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"OK"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Suppression de l\'espace de stockage partagé…"</string>
     <string name="share" msgid="4157615043345227321">"Partager"</string>
@@ -1967,7 +1959,7 @@
     <string name="app_category_image" msgid="7307840291864213007">"Photos et images"</string>
     <string name="app_category_social" msgid="2278269325488344054">"Réseaux sociaux et communication"</string>
     <string name="app_category_news" msgid="1172762719574964544">"Actualités et magazines"</string>
-    <string name="app_category_maps" msgid="6395725487922533156">"Plans et navigation"</string>
+    <string name="app_category_maps" msgid="6395725487922533156">"Cartes et navigation"</string>
     <string name="app_category_productivity" msgid="1844422703029557883">"Productivité"</string>
     <string name="app_category_accessibility" msgid="6643521607848547683">"Accessibilité"</string>
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Mémoire de l\'appareil"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> s\'exécute en arrière-plan et décharge la batterie. Appuyez ici pour en savoir plus."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> s\'exécute en arrière-plan depuis longtemps. Appuyez ici pour en savoir plus."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Vérifier les applis actives"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Impossible d\'accéder à l\'appareil photo depuis cet appareil"</string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 6db4a32..bd74bc6 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acceder ao teu calendario"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"enviar e consultar mensaxes de SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Ficheiros e documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"acceder a ficheiros e documentos do dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música e outro contido de audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"acceder a ficheiros de audio do teu dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos e vídeos"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Desbloquea a pantalla para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Detectouse unha impresión dixital parcial"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpa o sensor de impresión dixital e téntao de novo"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpa o sensor e téntao de novo"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Preme o sensor con firmeza"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O dedo moveuse demasiado lento. Téntao de novo."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proba con outra impresión dixital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hai demasiada luz"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia lixeiramente a posición do dedo en cada intento"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Non se recoñeceu a impresión dixital"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Preme o sensor con firmeza"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autenticouse a cara"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
     <string name="no_matches" msgid="6472699895759164599">"Non hai coincidencias"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Buscar na páxina"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# coincidencia}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Feito"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamento compartido…"</string>
     <string name="share" msgid="4157615043345227321">"Compartir"</string>
@@ -2102,11 +2094,11 @@
     <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de atallos de accesibilidade en pantalla"</string>
     <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Atallo de accesibilidade"</string>
     <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignorar panel despregable"</string>
-    <string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Botón direccional: arriba"</string>
-    <string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Botón direccional: abaixo"</string>
-    <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botón direccional: esquerda"</string>
-    <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Botón direccional: dereita"</string>
-    <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Botón direccional: centro"</string>
+    <string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Cruceta: arriba"</string>
+    <string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Cruceta: abaixo"</string>
+    <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: esquerda"</string>
+    <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Cruceta: dereita"</string>
+    <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Cruceta: centro"</string>
     <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> incluíuse no grupo RESTRINXIDO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> está executándose en segundo plano e consumindo batería. Toca para revisalo."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> leva moito tempo executándose en segundo plano. Toca para revisalo."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Comprobar aplicacións activas"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Non se pode acceder á cámara desde este dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index fe1b92b..8a4916b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS સંદેશા મોકલવાની અને જોવાની"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ફાઇલો અને દસ્તાવેજો"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"તમારા ડિવાઇસ પર ફાઇલો અને દસ્તાવેજો ઍક્સેસ કરો"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"મ્યુઝિક અને અન્ય ઑડિયો"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"તમારા ડિવાઇસ પર ઑડિયો ફાઇલો ઍક્સેસ કરવા માટે"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"ફોટા અને વીડિયો"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"આગળ વધવા માટે તમારું સ્ક્રીન લૉક દાખલ કરો"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"આંશિક ફિંગરપ્રિન્ટ મળી"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ફિંગરપ્રિન્ટ સેન્સર સાફ કરો અને ફરી પ્રયાસ કરો"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"સેન્સર સાફ કરો અને ફરી પ્રયાસ કરો"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"સેન્સર પર જોરથી દબાવો"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"આંગળી બહુ જ ધીમેથી ખસેડી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"અન્ય ફિંગરપ્રિન્ટ અજમાવી જુઓ"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"અતિશય પ્રકાશિત"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"દરેક વખતે સ્કૅનર પર તમારી આંગળીની સ્થિતિ સહેજ બદલતા રહો"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ફિંગરપ્રિન્ટ ઓળખી શકાઈ નથી"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"સેન્સર પર જોરથી દબાવો"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ચહેરા પ્રમાણિત"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"છોડો"</string>
     <string name="no_matches" msgid="6472699895759164599">"કોઈ મેળ નથી"</string>
     <string name="find_on_page" msgid="5400537367077438198">"પૃષ્ઠ પર શોધો"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# મૅચ}one{{total}માંથી #}other{{total}માંથી #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"થઈ ગયું"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"શેર કરેલ સ્ટોરેજ ભૂસી રહ્યાં છીએ…"</string>
     <string name="share" msgid="4157615043345227321">"શેર કરો"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> બૅકગ્રાઉન્ડમાં ચાલી રહી છે અને અતિશય બૅટરી વાપરી રહી છે. રિવ્યૂ કરવા માટે ટૅપ કરો."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> લાંબા સમયથી બૅકગ્રાઉન્ડમાં ચાલી રહી છે. રિવ્યૂ કરવા માટે ટૅપ કરો."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"સક્રિય ઍપ ચેક કરો"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"આ ડિવાઇસમાંથી કૅમેરા ઍક્સેસ કરી શકાતો નથી"</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3cf2137..b688fd2 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करें"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"मैसेज (एसएमएस)"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"मैसेज (एसएमएस) भेजें और देखें"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"फ़ाइलें और दस्तावेज़"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"अपने डिवाइस पर मौजूद फ़ाइलें और दस्तावेज़ ऐक्सेस करने की अनुमति दें"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"संगीत और अन्य ऑडियो"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"आपके डिवाइस पर मौजूद, ऑडियो फ़ाइलों का ऐक्सेस"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"फ़ोटो और वीडियो"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"जारी रखने के लिए, अपने स्क्रीन लॉक की पुष्टि करें"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"पूरा फ़िंगरप्रिंट पहचाना नहीं जा सका"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फ़िंगरप्रिंट प्रोसेस नहीं हो सका. कृपया दोबारा कोशिश करें."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"फ़िंगरप्रिंट सेंसर को साफ़ करके फिर से कोशिश करें"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"फ़िंगरप्रिंट सेंसर को साफ़ करके फिर से कोशिश करें"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"सेंसर को उंगली से ज़ोर से दबाएं"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"उंगली बहुत धीरे चलाई गई. कृपया फिर से कोशिश करें."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"किसी दूसरे फ़िंगरप्रिंट से कोशिश करें"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"बहुत रोशनी है"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"फ़िंगरप्रिंट सेट अप करते समय, अपनी उंगली को हर बार थोड़ी अलग स्थिति में रखें"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"फ़िंगरप्रिंट की पहचान नहीं हो पाई"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"सेंसर को उंगली से ज़ोर से दबाएं"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"अभी नहीं"</string>
     <string name="no_matches" msgid="6472699895759164599">"कोई मिलान नहीं"</string>
     <string name="find_on_page" msgid="5400537367077438198">"पेज पर ढूंढें"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# नतीजा}one{{total} में से #}other{{total} में से #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"हो गया"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"शेयर की गई मेमोरी हमेशा के लिए मिटाई जा रही है…"</string>
     <string name="share" msgid="4157615043345227321">"शेयर करें"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> बैकग्राउंड में चल रहा है और बैटरी खर्च कर रहा है. देखने के लिए टैप करें."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> बैकग्राउंड में बहुत देर से चल रहा है. देखने के लिए टैप करें."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"चालू ऐप्लिकेशन देखें"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"इस डिवाइस से कैमरे का इस्तेमाल नहीं किया जा सकता"</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 14139ea..fff937b 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -305,10 +305,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"pristupati kalendaru"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"slati i pregledavati SMS poruke"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Datoteke i dokumenti"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"pristup datotekama i dokumentima na vašem uređaju"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Glazba i druge audiodatoteke"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"pristup audiodatotekama na uređaju"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotografije i videozapisi"</string>
@@ -589,12 +587,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Upotrijebite zaključavanje zaslona da biste nastavili"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Otkriven je djelomični otisak prsta"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Očistite senzor otiska prsta i pokušajte ponovno"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Očistite senzor i pokušajte ponovno"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Čvrsto pritisnite senzor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Isprobajte drugi otisak prsta"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvijetlo"</string>
@@ -602,10 +597,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put lagano promijenite položaj prsta"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Otisak prsta nije prepoznat"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Čvrsto pritisnite senzor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je autentificirano"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string>
@@ -1509,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nema rezultata"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Pronađi na stranici"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# podudaranje}one{# od {total}}few{# od {total}}other{# od {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Gotovo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Brisanje dijeljene pohrane…"</string>
     <string name="share" msgid="4157615043345227321">"Dijeli"</string>
@@ -2264,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> izvodi se u pozadini i prazni bateriju. Dodirnite za pregled."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> dugo se izvodi u pozadini. Dodirnite za pregled."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Provjera aktivnih aplikacija"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Kameri se ne može pristupiti s ovog uređaja"</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f94f063..166a117 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -545,7 +545,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lehetővé teszi az alkalmazás számára, hogy közzétegye jelenlétét a közeli Bluetooth-eszközök számára"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"közeli, ultraszélessávú eszközök közötti relatív pozíció meghatározása"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Az alkalmazás meghatározhatja a közeli, ultraszélessávú eszközök közötti relatív pozíciót"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"műveleteket végezhet a közeli Wi‑Fi-eszközökkel"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"műveletek végrehajtása a közeli Wi‑Fi-eszközökkel"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Engedélyezi az alkalmazás számára, hogy közzétegye és meghatározza a közeli Wi-Fi-eszközök viszonylagos helyzetét, és csatlakozzon hozzájuk."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferált NFC fizetési szolgáltatási információk"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lehetővé teszi az alkalmazás számára preferált NFC fizetési szolgáltatási információk (pl. regisztrált alkalmazásazonosítók és útvonali cél) lekérését."</string>
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Kihagyás"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nincs találat"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Keresés az oldalon"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# egyezés}other{{total}/#}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Kész"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Közös tárhely tartalmának törlése…"</string>
     <string name="share" msgid="4157615043345227321">"Megosztás"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"A(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazás fut a háttérben, és meríti az akkumulátort. Koppintson az áttekintéshez."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"A(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazás már hosszú ideje fut a háttérben. Koppintson az áttekintéshez."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Aktív alkalmazások ellenőrzése"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Ezen az eszközön nem lehet hozzáférni a kamerához"</string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index ea6eddd..76ca7a9 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"օգտագործել օրացույցը"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"ուղարկել և դիտել SMS-ները"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Ֆայլեր և փաստաթղթեր"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"սարքի ֆայլերի և փաստաթղթերի օգտագործման թույլտվություն"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Երաժշտություն և այլ աուդիո նյութեր"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"բացել ձեր սարքում պահված աուդիո ֆայլերը"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Լուսանկարներ և տեսանյութեր"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Շարունակելու համար ապակողպեք էկրանը"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Մատնահետքն ամբողջությամբ չի սկանավորվել"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Մաքրեք մատնահետքերի սկաները և նորից փորձեք"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Մաքրեք սկաները և նորից փորձեք"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Շատ դանդաղ անցկացրիք մատը: Փորձեք նորից:"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Մատնահետքը չի ճանաչվել"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Դեմքը ճանաչվեց"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Բաց թողնել"</string>
     <string name="no_matches" msgid="6472699895759164599">"Համընկնում չկա"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Գտեք էջում"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# համընկնում}one{#՝ {total}-ից}other{#՝ {total}-ից}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Պատրաստ է"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Ընդհանուր հիշողությունը ջնջվում է…"</string>
     <string name="share" msgid="4157615043345227321">"Կիսվել"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> հավելվածն աշխատում է ֆոնային ռեժիմում և սպառում է մարտկոցի լիցքը։ Հպեք՝ դիտելու համար։"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> հավելվածը երկար ժամանակ աշխատում է ֆոնային ռեժիմում։ Հպեք՝ դիտելու համար։"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Ստուգել ակտիվ հավելվածները"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Հնարավոր չէ բացել տեսախցիկն այս սարքից"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index de24aa5..1868e37 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"mengakses kalender"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"mengirim dan melihat pesan SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"File &amp; dokumen"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"mengakses file dan dokumen di perangkat Anda"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musik &amp; audio lainnya"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"mengakses file audio di perangkat Anda"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Foto &amp; video"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Mengizinkan aplikasi menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan perangkat Wi-Fi di sekitar"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Mengizinkan aplikasi menampilkan informasi, menghubungkan, dan menentukan posisi relatif perangkat Wi-Fi di sekitar"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Mengizinkan aplikasi menampilkan, menghubungkan, dan menentukan posisi relatif perangkat Wi-Fi di sekitar"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasi Layanan Pembayaran NFC Pilihan"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Mengizinkan aplikasi untuk mendapatkan informasi layanan pembayaran NFC pilihan seperti bantuan terdaftar dan tujuan rute."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrol NFC"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Masukkan kunci layar untuk melanjutkan"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Sebagian sidik jari terdeteksi"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses sidik jari. Coba lagi."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Bersihkan sensor sidik jari lalu coba lagi"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Bersihkan sensor lalu coba lagi"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tekan sensor dengan kuat"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Jari digerakkan terlalu lambat. Coba lagi."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Coba sidik jari lain"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ubah sedikit posisi jari di setiap percobaan"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Sidik jari tidak dikenali"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Tekan sensor dengan kuat"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah diautentikasi"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Lewati"</string>
     <string name="no_matches" msgid="6472699895759164599">"Tidak ada kecocokan"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Temukan pada halaman"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# kecocokan}other{# dari {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Selesai"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Menghapus penyimpanan bersama…"</string>
     <string name="share" msgid="4157615043345227321">"Bagikan"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> berjalan di latar belakang dan menghabiskan daya baterai. Ketuk untuk meninjau."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> berjalan di latar belakang dalam waktu yang lama. Ketuk untuk meninjau."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Periksa aplikasi aktif"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Tidak dapat mengakses kamera dari perangkat ini"</string>
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 4b2f371..07560e1 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"fá aðgang að dagatalinu þínu"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"senda og skoða SMS-skilaboð"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Skrár og skjöl"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"fá aðgang að skrám og skjölum í tækinu þínu"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Tónlist og annað hljóð"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"fá aðgang að hljóðskrám í tækinu þínu"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Myndir og myndskeið"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Sláðu inn skjálásinn þinn til að halda áfram"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Hluti fingrafars greindist"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Hreinsaðu fingrafaralesarann og reyndu aftur"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Hreinsaðu lesarann og reyndu aftur"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Ýttu ákveðið á lesarann"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prófaðu annað fingrafar"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Of bjart"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Breyttu stöðu fingursins örlítið í hvert skipti"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Ekki þekkt fingrafar"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Ýttu ákveðið á lesarann"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Andlit staðfest"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Sleppa"</string>
     <string name="no_matches" msgid="6472699895759164599">"Engar samsvaranir"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Finna á síðu"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# samsvörun}one{# af {total}}other{# af {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Lokið"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Eyðir samnýttri geymslu…"</string>
     <string name="share" msgid="4157615043345227321">"Deila"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> keyrir í bakgrunni og eyðir rafhlöðuorku. Ýttu til að skoða."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> hefur keyrt lengi í bakgrunni. Ýttu til að skoða."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Skoða virk forrit"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Aðgangur að myndavél er ekki tiltækur í þessu tæki"</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b246d79..6ee8ba3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -537,11 +537,11 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Consente all\'applicazione di visualizzare la configurazione del Bluetooth sul tablet e di stabilire e accettare connessioni con dispositivi accoppiati."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Consente all\'app di visualizzare la configurazione del Bluetooth del dispositivo Android TV, nonché di stabilire e accettare connessioni con dispositivi accoppiati."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Consente all\'applicazione di visualizzare la configurazione del Bluetooth sul telefono e di stabilire e accettare connessioni con dispositivi accoppiati."</string>
-    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Rilevamento/accopp. dispositivi Bluetooth vicini"</string>
+    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Rilevamento e accoppiamento di dispositivi Bluetooth vicini"</string>
     <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Consente all\'app di rilevare e accoppiare dispositivi Bluetooth nelle vicinanze"</string>
     <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Connessione a dispositivi Bluetooth accoppiati"</string>
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Consente all\'app di connettersi ai dispositivi Bluetooth accoppiati"</string>
-    <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"trasmettere annunci a dispositivi Bluetooth vicini"</string>
+    <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Trasmissione di annunci a dispositivi Bluetooth vicini"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Consente all\'app di trasmettere annunci ai dispositivi Bluetooth nelle vicinanze"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Possibilità di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Consenti all\'app di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
@@ -682,7 +682,7 @@
     <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Consente a un\'applicazione di modificare le impostazioni di sincronizzazione per un account. Ad esempio, può servire per attivare la sincronizzazione dell\'applicazione Persone con un account."</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"lettura statistiche di sincronizz."</string>
     <string name="permdesc_readSyncStats" msgid="3867809926567379434">"Consente a un\'applicazione di leggere le statistiche di sincronizzazione per un account, incluse la cronologia degli eventi di sincronizzazione e la quantità di dati sincronizzati."</string>
-    <string name="permlab_sdcardRead" msgid="5791467020950064920">"lettura dei contenuti dell\'archivio condiviso"</string>
+    <string name="permlab_sdcardRead" msgid="5791467020950064920">"Lettura dei contenuti dell\'archivio condiviso"</string>
     <string name="permdesc_sdcardRead" msgid="6872973242228240382">"Consente all\'app di leggere i contenuti del tuo archivio condiviso."</string>
     <string name="permlab_readMediaAudio" msgid="8723513075731763810">"Lettura dei file audio dallo spazio di archiviazione condiviso"</string>
     <string name="permdesc_readMediaAudio" msgid="5299772574434619399">"Consente all\'app di leggere i file audio dal tuo spazio di archiviazione condiviso."</string>
@@ -690,7 +690,7 @@
     <string name="permdesc_readMediaVideo" msgid="3846400073770403528">"Consente all\'app di leggere i file video dal tuo spazio di archiviazione condiviso."</string>
     <string name="permlab_readMediaImage" msgid="1507059005825769856">"Lettura dei file immagine dallo spazio di archiviazione condiviso"</string>
     <string name="permdesc_readMediaImage" msgid="8328052622292457588">"Consente all\'app di leggere i file immagine dal tuo spazio di archiviazione condiviso."</string>
-    <string name="permlab_sdcardWrite" msgid="4863021819671416668">"modifica/eliminazione dei contenuti dell\'archivio condiviso"</string>
+    <string name="permlab_sdcardWrite" msgid="4863021819671416668">"Modifica/eliminazione dei contenuti dell\'archivio condiviso"</string>
     <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Consente all\'app di modificare i contenuti del tuo archivio condiviso."</string>
     <string name="permlab_use_sip" msgid="8250774565189337477">"invio/ricezione di chiamate SIP"</string>
     <string name="permdesc_use_sip" msgid="3590270893253204451">"Consente all\'app di effettuare e ricevere chiamate SIP."</string>
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Salta"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nessuna corrispondenza"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Trova nella pagina"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# corrispondenza}one{# di {total}}other{# di {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Fine"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Cancellazione archivio condiviso…"</string>
     <string name="share" msgid="4157615043345227321">"Condividi"</string>
@@ -2023,16 +2022,11 @@
     <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DISINSTALLA"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"APRI COMUNQUE"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"App dannosa rilevata"</string>
-    <!-- no translation found for log_access_confirmation_title (3143035474800851565) -->
-    <skip />
-    <!-- no translation found for log_access_confirmation_allow (143157286283302512) -->
-    <skip />
-    <!-- no translation found for log_access_confirmation_deny (7685790957455099845) -->
-    <skip />
-    <!-- no translation found for log_access_confirmation_body (7599059550906238538) -->
-    <skip />
-    <!-- no translation found for log_access_do_not_show_again (1058690599083091552) -->
-    <skip />
+    <string name="log_access_confirmation_title" msgid="3143035474800851565">"Richiesta di accesso al log di sistema"</string>
+    <string name="log_access_confirmation_allow" msgid="143157286283302512">"Solo questa volta"</string>
+    <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Non consentire"</string>
+    <string name="log_access_confirmation_body" msgid="7599059550906238538">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> richiede i log di sistema per eseguire debug funzionali. Questi log potrebbero contenere informazioni scritte dalle app e dai servizi sul tuo dispositivo."</string>
+    <string name="log_access_do_not_show_again" msgid="1058690599083091552">"Non mostrare più"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"L\'app <xliff:g id="APP_0">%1$s</xliff:g> vuole mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>"</string>
     <string name="screenshot_edit" msgid="7408934887203689207">"Modifica"</string>
     <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"La vibrazione sarà attiva per chiamate e notifiche"</string>
@@ -2125,10 +2119,8 @@
     <string name="resolver_switch_on_work" msgid="463709043650610420">"Tocca per attivare"</string>
     <string name="resolver_no_work_apps_available" msgid="3298291360133337270">"Nessuna app di lavoro"</string>
     <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"Nessuna app personale"</string>
-    <!-- no translation found for miniresolver_open_in_personal (3874522693661065566) -->
-    <skip />
-    <!-- no translation found for miniresolver_open_in_work (4415223793669536559) -->
-    <skip />
+    <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Aprire <xliff:g id="APP">%s</xliff:g> nel tuo profilo personale?"</string>
+    <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Aprire <xliff:g id="APP">%s</xliff:g> nel tuo profilo di lavoro?"</string>
     <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Usa il browser personale"</string>
     <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Usa il browser di lavoro"</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"PIN di sblocco rete SIM"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> è in esecuzione in background e sta consumando la batteria. Tocca per controllare."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> è in esecuzione in background da molto tempo. Tocca per controllare."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verifica le app attive"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Impossibile accedere alla fotocamera da questo dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7cd023f..ea61e36 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"גישה אל היומן"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"‏שליחה והצגה של הודעות SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"קבצים ומסמכים"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"גישה לקבצים ולמסמכים במכשיר"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"מוזיקה וסוגי אודיו אחרים"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"גישה לקובצי אודיו במכשיר"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"תמונות וסרטונים"</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"יש לבטל את נעילת המסך כדי להמשיך"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"זוהתה טביעת אצבע חלקית"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"לא ניתן היה לעבד את טביעת האצבע. אפשר לנסות שוב."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"עליך לנקות את חיישן טביעות האצבע ולנסות שוב"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"עליך לנקות את החיישן ולנסות שוב"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"צריך ללחוץ חזק על החיישן"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"הזזת את האצבע לאט מדי. יש לנסות שוב."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"יש להשתמש בטביעת אצבע אחרת"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"בהיר מדי"</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"צריך לשנות מעט את תנוחת האצבע בכל פעם"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"טביעת האצבע לא זוהתה"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"צריך ללחוץ חזק על החיישן"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"זיהוי הפנים בוצע"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"דילוג"</string>
     <string name="no_matches" msgid="6472699895759164599">"אין התאמות"</string>
     <string name="find_on_page" msgid="5400537367077438198">"חיפוש בדף"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{התאמה אחת}two{# מתוך {total}}many{# מתוך {total}}other{# מתוך {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"סיום"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"בתהליך מחיקה של אחסון משותף…"</string>
     <string name="share" msgid="4157615043345227321">"שיתוף"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת ברקע ומרוקנת את הסוללה. יש להקיש כדי לבדוק."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת ברקע במשך הרבה זמן. יש להקיש כדי לבדוק."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"כדאי לבדוק את האפליקציות הפעילות"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"לא ניתן לגשת למצלמה מהמכשיר הזה"</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 0c70187..8bb6b0f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -545,7 +545,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"付近の Bluetooth デバイスへの広告の配信をアプリに許可します"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"付近の Ultra Wideband デバイス間の相対位置の特定"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"付近の Ultra Wideband デバイス間の相対位置の特定をアプリに許可します"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"付近の Wi-Fi デバイスとのやり取り"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"付近の Wi-Fi デバイスとの通信"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"付近の Wi-Fi デバイスについて、情報の表示、接続、相対位置の確認をアプリに許可します"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"優先される NFC お支払いサービスの情報"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"登録されている支援やルートの目的地など、優先される NFC お支払いサービスの情報を取得することをアプリに許可します。"</string>
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"スキップ"</string>
     <string name="no_matches" msgid="6472699895759164599">"該当なし"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ページ内を検索"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{一致: # 件}other{#/{total} 件}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"完了"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"共有ストレージを消去しています…"</string>
     <string name="share" msgid="4157615043345227321">"共有"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> がバックグラウンドで実行され、バッテリーを消費しています。タップしてご確認ください。"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> がバックグラウンドで長時間実行されています。タップしてご確認ください。"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"有効なアプリをチェック"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"このデバイスからはカメラにアクセスできません"</string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ae3f31c..0a41b20 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"გამოტოვება"</string>
     <string name="no_matches" msgid="6472699895759164599">"შესატყვისები არ არის."</string>
     <string name="find_on_page" msgid="5400537367077438198">"გვერდზე ძებნა"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# დამთხვევა}other{{total}-დან #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"დასრულდა"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"მიმდინარეობს ზიარი მეხსიერების ამოშლა…"</string>
     <string name="share" msgid="4157615043345227321">"გაზიარება"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია ფონურ რეჟიმში და იყენებს ბატარეას. შეეხეთ გადასახედად."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ფონურ რეჟიმში დიდი ხანია გაშვებულია. შეეხეთ გადასახედად."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"აქტიური აპების შემოწმება"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"კამერაზე წვდომა ამ მოწყობილობიდან ვერ მოხერხდება"</string>
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 439060f..fb6ffcf 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"күнтізбеге кіру"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS хабарларын жіберу және көру"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файлдар мен құжаттар"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"құрылғыдағы файлдар мен құжаттарды пайдалану"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музыка және басқа аудио"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"құрылғыдағы аудиофайлдарды пайдалану"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Фотосуреттер және бейнелер"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Жалғастыру үшін экран құлпын енгізіңіз."</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Саусақ ізі жартылай анықталды."</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Саусақ ізін оқу сканерін тазалап, әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Сканерді тазалап, әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Сканерге қатты басыңыз."</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Саусағыңызды тым баяу қозғалттыңыз. Әрекетті қайталап көріңіз."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Басқа саусақ ізін байқап көріңіз."</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Тым жарық."</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Саусағыңыздың қалпын аздап өзгертіп тұрыңыз."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Саусақ ізі танылмады."</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Сканерге қатты басыңыз."</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Бет танылды"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Өткізіп жіберу"</string>
     <string name="no_matches" msgid="6472699895759164599">"Сәйкес табылмады"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Беттен табу"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# сәйкестік}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Дайын"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Ортақ жад тазартылуда…"</string>
     <string name="share" msgid="4157615043345227321">"Бөлісу"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> қолданбасы фондық режимде жұмыс істеуде және батарея жұмсауда. Көру үшін түртіңіз."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> қолданбасы ұзақ уақыт бойы фондық режимде жұмыс істеуде. Көру үшін түртіңіз."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Белсенді қолданбаларды тексеру"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Бұл құрылғыдан камераны пайдалану мүмкін емес."</string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 49f2255..7e02b8b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ចូលប្រើប្រិតិទិនរបស់អ្នក"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"សារ SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"ផ្ញើ និងមើលសារ SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ឯកសារ"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"ចូលប្រើ​ឯកសារ​នៅលើ​ឧបករណ៍​របស់អ្នក"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"តន្ត្រី និងសំឡេងផ្សេងទៀត"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"ចូលប្រើឯកសារសំឡេងនៅលើឧបករណ៍របស់អ្នក"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"រូបថត និងវីដេអូ"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"បញ្ចូលការចាក់សោអេក្រង់របស់អ្នក ដើម្បីបន្ត"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"បានសម្គាល់​ស្នាមម្រាមដៃដោយផ្នែក"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"សម្អាត​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ រួចព្យាយាម​ម្ដងទៀត"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"សម្អាត​ឧបករណ៍​ចាប់សញ្ញា រួចព្យាយាម​ម្ដងទៀត"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"សង្កត់លើ​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យណែន"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ចលនាម្រាមដៃយឺតពេកហើយ។ សូមព្យាយាមម្តងទៀត។"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"សាកល្បងប្រើ​ស្នាមម្រាមដៃផ្សេងទៀត"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ភ្លឺពេក"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ប្ដូរទីតាំងម្រាមដៃ​របស់អ្នកតិចៗ​គ្រប់ពេល"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"មិនស្គាល់ស្នាមម្រាមដៃទេ"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"សង្កត់លើ​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យណែន"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"បាន​ផ្ទៀង​ផ្ទាត់​ស្នាម​ម្រាមដៃ"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"បានផ្ទៀងផ្ទាត់​មុខ"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់​មុខ សូម​ចុច​បញ្ជាក់"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"រំលង"</string>
     <string name="no_matches" msgid="6472699895759164599">"គ្មាន​ការ​ផ្គូផ្គង"</string>
     <string name="find_on_page" msgid="5400537367077438198">"រក​ក្នុង​ទំព័រ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{ត្រូវគ្នា #}other{# នៃ {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"រួចរាល់"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"កំពុងលុបទំហំផ្ទុករួម…"</string>
     <string name="share" msgid="4157615043345227321">"ចែក​រំលែក​"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> កំពុងដំណើរការនៅផ្ទៃខាងក្រោយ និងធ្វើឱ្យអស់ថ្មលឿន។ សូមចុច ដើម្បី​ពិនិត្យមើល។"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> កំពុងដំណើរការនៅផ្ទៃខាងក្រោយអស់រយៈពេលយូរហើយ។ សូមចុច ដើម្បី​ពិនិត្យមើល។"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ពិនិត្យមើលកម្មវិធីសកម្ម"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"មិនអាចចូលប្រើកាមេរ៉ាពីឧបករណ៍នេះបានទេ"</string>
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index b73b96f..c2c5078 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ಸ್ಕಿಪ್‌"</string>
     <string name="no_matches" msgid="6472699895759164599">"ಯಾವುದೇ ಹೊಂದಿಕೆಗಳಿಲ್ಲ"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ಪುಟದಲ್ಲಿ ಹುಡುಕಿ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# ಹೊಂದಾಣಿಕೆ}one{{total} ರಲ್ಲಿ #}other{{total} ರಲ್ಲಿ #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ಮುಗಿದಿದೆ"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"ಹಂಚಲಾದ ಸಂಗ್ರಹಣೆಯನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="share" msgid="4157615043345227321">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿದೆ ಹಾಗೂ ಬ್ಯಾಟರಿಯನ್ನು ಹೆಚ್ಚು ಬಳಸುತ್ತಿದೆ. ಪರಿಶೀಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ಬಹಳ ಸಮಯದಿಂದ ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿದೆ. ಪರಿಶೀಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ಸಕ್ರಿಯ ಆ್ಯಪ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ಈ ಸಾಧನದಿಂದ ಕ್ಯಾಮರಾ ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index a7f8cb4..f60cf0d 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"캘린더에 액세스"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS 메시지 전송 및 보기"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"파일 및 문서"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"기기의 파일 및 문서에 액세스"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"음악 및 기타 오디오"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"기기에 있는 오디오 파일에 액세스"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"사진 및 동영상"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"계속하려면 화면 잠금용 사용자 인증 정보를 입력하세요"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"지문의 일부만 감지됨"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"지문 센서를 닦은 후 다시 시도해 보세요."</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"센서를 닦은 후 다시 시도해 보세요."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"센서 위에 손가락을 좀 더 오래 올려놓으세요."</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"다른 지문으로 시도"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"너무 밝음"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문을 등록할 때마다 손가락을 조금씩 이동하세요."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"지문이 인식되지 않습니다."</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"센서 위에 손가락을 좀 더 오래 올려놓으세요."</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"건너뛰기"</string>
     <string name="no_matches" msgid="6472699895759164599">"검색결과 없음"</string>
     <string name="find_on_page" msgid="5400537367077438198">"페이지에서 찾기"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{#개 일치}other{총 {total}개 중 #번째}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"완료"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"공유 저장공간 지우는 중…"</string>
     <string name="share" msgid="4157615043345227321">"공유"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> 앱이 백그라운드에서 실행 중이며 배터리를 소모하고 있습니다. 확인하려면 탭하세요."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> 앱이 백그라운드에서 오랫동안 실행 중입니다. 확인하려면 탭하세요."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"활성 상태의 앱 확인"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"이 기기에서 카메라에 액세스할 수 없습니다."</string>
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 577626d..512ecfd 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"жылнаамаңызды пайдалануу"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS билдирүүлөрдү жиберүү жана көрсөтүү"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файлдар жана документтер"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"түзмөгүңүздөгү файлдары жана документтерди колдонуу"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музыка жана башка аудио"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"түзмөгүңүздөгү аудио файлдарга мүмкүнчүлүк алуу"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Сүрөттөр жана видеолор"</string>
@@ -454,9 +452,9 @@
     <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Бул колдонмо каалаган убакта микрофон менен аудио файлдарды жаздыра алат."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM-картага буйруктарды жөнөтүү"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Колдонмого SIM-картага буйруктарды жөнөтүү мүмкүнчүлүгүн берет. Бул абдан кооптуу."</string>
-    <string name="permlab_activityRecognition" msgid="1782303296053990884">"кыймыл-аракетти аныктоо"</string>
+    <string name="permlab_activityRecognition" msgid="1782303296053990884">"Кыймыл-аракетти аныктоо"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Бул колдонмо кыймыл-аракетиңизди аныктап турат."</string>
-    <string name="permlab_camera" msgid="6320282492904119413">"сүрөт жана видео тартуу"</string>
+    <string name="permlab_camera" msgid="6320282492904119413">"Сүрөт жана видео тартуу"</string>
     <string name="permdesc_camera" msgid="5240801376168647151">"Бул колдонмо иштеп жатканда камера менен сүрөт же видеолорду тарта алат."</string>
     <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Фондо сүрөткө жана видеого тартат"</string>
     <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Бул колдонмо каалаган убакта камера менен сүрөт же видеолорду тарта алат."</string>
@@ -539,16 +537,16 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Колдонмого планшеттин Bluetooth конфигурацияларын көрүү, жупталган түзмөктөр менен байланыш түзүү жана кабыл алуу уруксатын берет."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Android TV түзмөгүңүздөгү Bluetooth конфигурациясын көрүп, жупташтырылган түзмөктөргө туташууга жана туташуу сурамын кабыл алууга колдонмого уруксат берет."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Колдонмого телефондун Bluetooth конфигурацияларын көрүү, жупташкан түзмөктөр менен туташуу түзүү жана кабыл алуу уруксатын берет."</string>
-    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"жакын жердеги Bluetooth түзмөктөрүн аныктоо жана жупташтыруу"</string>
+    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Жакын жердеги Bluetooth түзмөктөрүн таап, туташуу"</string>
     <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Колдонмого жакын жердеги Bluetooth түзмөктөрүн аныктап, жупташтырууга уруксат берет"</string>
-    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"жупташтырылган Bluetooth түзмөктөрү"</string>
-    <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Колдонмого жупташтырылган Bluetooth түзмөктөрү менен байланышууга уруксат берет"</string>
+    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Туташып турган Bluetooth түзмөктөрүнө байланышуу"</string>
+    <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Колдонмого туташкан Bluetooth түзмөктөрүнө байланышканга уруксат берет"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"жакын жердеги Bluetooth түзмөктөрүнө жарнамалоо"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Колдонмого жакын жердеги Bluetooth түзмөктөрүнө жарнама көрсөтүүгө мүмкүндүк берет"</string>
-    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктоо"</string>
+    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктоо"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Колдонмо кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктай алат"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"жакын жердеги Wi‑Fi түзмөктөрү менен байланышуу"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Колдонмого жарнама көрсөтүүгө, байланышууга жана жакын жердеги Wi‑Fi түзмөктөрүн аныктоого мүмкүндүк берет"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Жакын жердеги Wi‑Fi түзмөктөрүнө байланышуу"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Колдонмо жакын жердеги Wi-Fi түзмөктөргө туташып, алардын жайгашкан жерин аныктап, ар кандай нерселерди өткөрө алат."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Тандалган NFC төлөм кызматы жөнүндө маалымат"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Колдонмого катталган жардам же көздөлгөн жерге маршрут сыяктуу тандалган nfc төлөм кызматы жөнүндө маалыматты алууга уруксат берүү."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication көзөмөлү"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Улантуу үчүн экрандын кулпусун киргизиңиз"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Манжа изи жарым-жартылай аныкталды"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Манжа изи иштелбей койду. Кайталап көрүңүз."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Манжа изинин сенсорун тазалап, кайра аракет кылыңыз"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Сенсорду тазалап, кайра аракет кылыңыз"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Сенсорду катуу басыңыз"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Манжа өтө жай жылды. Кайталап көрүңүз."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Башка манжа изин байкап көрүңүз"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Өтө жарык"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Манжаңыздын абалын ар жолкусунда бир аз өзгөртүп туруңуз"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Манжа изи таанылган жок"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Сенсорду катуу басыңыз"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Жүздүн аныктыгы текшерилди"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Өткөрүп жиберүү"</string>
     <string name="no_matches" msgid="6472699895759164599">"Дал келүүлөр жок"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Барактан табуу"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# дал келди}other{{total} ичинен # дал келди}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Даяр"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Жалпы сактагыч тазаланууда…"</string>
     <string name="share" msgid="4157615043345227321">"Бөлүшүү"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> фондо иштеп, батареяны отургузуп жатат. Көрүү үчүн таптап коюңуз."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> колдонмосу көп убакыттан бери фондо иштеп жатат. Көрүү үчүн таптап коюңуз."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Жигердүү колдонмолорду карап чыгуу"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Бул түзмөктөгү камераны колдонууга болбойт"</string>
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 090bde9..e631dfb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ຂ້າມ"</string>
     <string name="no_matches" msgid="6472699895759164599">"ບໍ່ພົບຜົນການຊອກຫາ"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ຊອກໃນໜ້າ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{ກົງກັນ # ລາຍການ}other{# ຈາກທັງໝົດ {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ແລ້ວໆ"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"ກຳລັງລຶບບ່ອນຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນ…"</string>
     <string name="share" msgid="4157615043345227321">"ແບ່ງປັນ"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກໃນພື້ນຫຼັງ ແລະ ໃຊ້ແບັດເຕີຣີຫຼາຍ. ແຕະເພື່ອກວດສອບ."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກໃນພື້ນຫຼັງເປັນເວລາດົນແລ້ວ. ແຕະເພື່ອກວດສອບ."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ກວດສອບແອັບທີ່ເຄື່ອນໄຫວ"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຈາກອຸປະກອນນີ້ໄດ້"</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f89c429..6b3311c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1503,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Praleisti"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nėra atitikčių"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Ieškoti puslapyje"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# atitiktis}one{# iš {total}}few{# iš {total}}many{# iš {total}}other{# iš {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Atlikta"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Ištrinama bendrinama saugykla…"</string>
     <string name="share" msgid="4157615043345227321">"Bendrinti"</string>
@@ -2258,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"„<xliff:g id="APP">%1$s</xliff:g>“ veikia fone ir eikvoja akumuliatoriaus energiją. Palieskite ir peržiūrėkite."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"„<xliff:g id="APP">%1$s</xliff:g>“ ilgą laiką veikia fone. Palieskite ir peržiūrėkite."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Peržiūrėkite aktyvias programas"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nepavyksta pasiekti fotoaparato iš šio įrenginio"</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 030144b..8924fa4 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -305,10 +305,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"piekļūt jūsu kalendāram"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Īsziņas"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"sūtīt un skatīt īsziņas"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Faili un dokumenti"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"piekļuve failiem un dokumentiem jūsu ierīcē"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Mūzika un cits audio saturs"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"piekļūt audio failiem jūsu ierīcē"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotoattēli un videoklipi"</string>
@@ -589,12 +587,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Lai turpinātu, ievadiet ekrāna bloķēšanas informāciju"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Tika konstatēts nepilnīgs pilna nospiedums"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Notīriet pirkstu nospiedumu sensoru un mēģiniet vēlreiz"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Notīriet sensoru un mēģiniet vēlreiz"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Stingri spiediet pirkstu pie sensora"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Izmēģiniet citu pirksta nospiedumu"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Pārāk spilgts"</string>
@@ -602,10 +597,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Katru reizi mazliet mainiet pirksta pozīciju."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Pirksta nospiedums netika atpazīts"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Stingri spiediet pirkstu pie sensora"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Seja autentificēta"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string>
@@ -1509,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Izlaist"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nav atbilstību"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Atrast lapā"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# atbilstība}zero{# no {total}}one{# no {total}}other{# no {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Gatavs"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Notiek koplietotās krātuves dzēšana…"</string>
     <string name="share" msgid="4157615043345227321">"Kopīgot"</string>
@@ -2264,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> darbojas fonā un patērē akumulatora enerģiju. Pieskarieties, lai to pārskatītu."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ilgi darbojas fonā. Pieskarieties, lai to pārskatītu."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Pārbaudiet aktīvās lietotnes"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nevar piekļūt kamerai no šīs ierīces"</string>
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a67a522..518b4e3 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"пристапува до календарот"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"испраќа и прикажува SMS-пораки"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Датотеки и документи"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"да пристапува до датотеки и документи на уредот"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музика и друго аудио"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"да пристапува до аудиодатотеки на вашиот уред"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Фотографии и видеа"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Внесете го заклучувањето на екранот за да продолжите"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Откриен е делумен отпечаток"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатокот не може да се обработи. Обидете се повторно."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Исчистете го сензорот за отпечатоци и обидете се повторно"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Исчистете го сензорот и обидете се повторно"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Цврсто притиснете на сензорот"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте со друг отпечаток"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Премногу светло"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Менувајте ја положбата на прстот по малку секој пат"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечатокот не е препознаен"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Цврсто притиснете на сензорот"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е проверено"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Прескокни"</string>
     <string name="no_matches" msgid="6472699895759164599">"Нема совпаѓања"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Пронајди на страница"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# совпаѓање}one{# од {total}}other{# од {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Бришење споделена меморија…"</string>
     <string name="share" msgid="4157615043345227321">"Сподели"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> се извршува во заднина и ја троши батеријата. Допрете за да прегледате."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> се извршува во заднина веќе долго време. Допрете за да прегледате."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверете ги активните апликации"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Не може да се пристапи до камерата од уредов"</string>
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ec027b9..0e2ff33 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -546,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കൂ"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങളുമായി ഇടപഴകുക"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"സമീപമുള്ള വൈഫൈ ഉപകരണത്തെ കാണിക്കാനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും ആപ്പിനെ അനുവദിക്കൂ"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങൾ കാണിക്കാനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും ആപ്പിനെ അനുവദിക്കൂ"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"റൂട്ട് ലക്ഷ്യസ്ഥാനം, രജിസ്‌റ്റർ ചെയ്തിരിക്കുന്ന സഹായങ്ങൾ എന്നിവ പോലുള്ള, തിരഞ്ഞെടുത്ത NFC പേയ്‌മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ ലഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"സമീപ ഫീൽഡുമായുള്ള ആശയവിനിമയം നിയന്ത്രിക്കുക"</string>
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ഒഴിവാക്കുക"</string>
     <string name="no_matches" msgid="6472699895759164599">"പൊരുത്തപ്പെടലുകൾ ഒന്നുമില്ല"</string>
     <string name="find_on_page" msgid="5400537367077438198">"പേജിൽ കണ്ടെത്തുക"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# പൊരുത്തം}other{{total}-ൽ #-ാമത്തേത്}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"പൂർത്തിയായി"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"പങ്കിടുന്ന സ്‌റ്റോറേജ് മായ്‌ക്കുന്നു…"</string>
     <string name="share" msgid="4157615043345227321">"പങ്കിടുക"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ആപ്പ് പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു, ഇത് ബാറ്ററി ഉപയോഗിച്ചുതീർക്കുന്നു. അവലോകനം ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"പശ്ചാത്തലത്തിൽ <xliff:g id="APP">%1$s</xliff:g> ആപ്പ് ഒരുപാട് നേരമായി റൺ ചെയ്യുന്നു. അവലോകനം ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"സജീവമായ ആപ്പുകൾ പരിശോധിക്കുക"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ഈ ഉപകരണത്തിൽ നിന്ന് ക്യാമറ ആക്‌സസ് ചെയ്യാനാകില്ല"</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 5681de0..b9df517 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Алгасах"</string>
     <string name="no_matches" msgid="6472699895759164599">"Илэрц алга"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Хуудаснаас олох"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# тааруулах}other{{total}-н #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Дуусгах"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Хуваалцсан хадгалах санг устгаж байна…"</string>
     <string name="share" msgid="4157615043345227321">"Хуваалцах"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> дэвсгэрт ажиллаж байгаа бөгөөд батарейг дуусгаж байна. Хянахын тулд товшино уу."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> дэвсгэрт удаан хугацааны турш ажиллаж байна. Хянахын тулд товшино уу."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Идэвхтэй аппуудыг шалгах"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Энэ төхөөрөмжөөс камер луу нэвтрэх боломжгүй байна"</string>
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1c434bb..86e59e3 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"वगळा"</string>
     <string name="no_matches" msgid="6472699895759164599">"कोणत्याही जुळण्या नाहीत"</string>
     <string name="find_on_page" msgid="5400537367077438198">"पेजवर शोधा"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# जुळणी}other{{total} पैकी #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"पूर्ण केले"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"शेअर केलेले स्टोरेज मिटवत आहे…"</string>
     <string name="share" msgid="4157615043345227321">"शेअर करा"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> हे बॅकग्राउंडमध्ये रन होत आहे आणि बॅटरी संपवत आहे. पुनरावलोकनासाठी टॅप करा."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> हे बऱ्याच कालावधीपासून बॅकग्राउंडमध्ये रन होत आहे. पुनरावलोकनासाठी टॅप करा."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ॲक्टिव्ह ॲप्स पहा"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"या डिव्हाइसवरून कॅमेरा अ‍ॅक्सेस करू शकत नाही"</string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index b4ce57c..543b8be 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"mengakses kalendar"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"menghantar dan melihat mesej SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fail &amp; dokumen"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"akses fail dan dokumen pada peranti anda"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muzik &amp; audio lain"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"berikan akses fail audio pada peranti anda"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Foto &amp; video"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Masukkan kunci skrin untuk teruskan"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Cap jari separa dikesan"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Bersihkan penderia cap jari dan cuba lagi"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Bersihkan penderia dan cuba lagi"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tekan dengan kuat pada penderia"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Cuba cap jari lain"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Tukar sedikit kedudukan jari anda setiap kali pergerakan dilakukan"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Cap jari tidak dikenali"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Tekan dengan kuat pada penderia"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah disahkan"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Langkau"</string>
     <string name="no_matches" msgid="6472699895759164599">"Tiada padanan"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Cari di halaman"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# padanan}other{# daripada {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Selesai"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Memadamkan storan kongsi…"</string>
     <string name="share" msgid="4157615043345227321">"Kongsi"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan di latar belakang dan menghabiskan bateri. Ketik untuk menyemak."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g>sedang berjalan di latar belakang untuk masa yang lama. Ketik untuk menyemak."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Semak apl aktif"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Tidak dapat mengakses kamera daripada peranti ini"</string>
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index acbd498..5da730b 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS စာတိုစနစ်"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS စာများကို ပို့ကာ ကြည့်မည်"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ဖိုင်နှင့် မှတ်တမ်းများ"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"သင့်စက်ရှိ ဖိုင်နှင့် မှတ်တမ်းများ သုံးခွင့်"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"သီချင်းနှင့် အခြားအသံဖိုင်"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"သင့်စက်ပေါ်ရှိ အသံဖိုင်များကို သုံးနိုင်သည်"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"ဓာတ်ပုံနှင့် ဗီဒီယိုများ"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"အနီးတစ်ဝိုက်ရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား ဆက်စပ်နေရာကို သတ်မှတ်ခြင်း"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"အနီးတစ်ဝိုက်ရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား ဆက်စပ်နေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုမည်"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"အနီးရှိ Wi-Fi စက်များနှင့် ပြန်လှန်တုံ့ပြန်ခြင်း"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"အက်ပ်ကို ကြော်ငြာရန်၊ ချိတ်ဆက်ရန်နှင့် အနီးတစ်ဝိုက်ရှိ Wi-Fi စက်များ၏ ဆက်စပ်နေရာကို သတ်မှတ်ရန် ခွင့်ပြုနိုင်သည်"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ကြော်ငြာရန်၊ ချိတ်ဆက်ရန်နှင့် အနီးတစ်ဝိုက်ရှိ Wi-Fi စက်များ၏ နေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ဦးစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များ"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"အက်ပ်အား ဦစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များဖြစ်သည့် မှတ်ပုံတင်ထားသော အကူအညီများနှင့် သွားလာရာ လမ်းကြောင်းတို့ကို ရယူရန် ခွင့်ပြုသည်။"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communicationအား ထိန်းချုပ်ရန်"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ရှေ့ဆက်ရန် သင်၏ဖန်သားပြင် လော့ခ်ချခြင်းကို ထည့်ပါ"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"လက်ဗွေတစ်စိတ်တစ်ပိုင်းကို ရှာတွေ့သည်"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"လက်ဗွေယူ၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"လက်ဗွေ အာရုံခံကိရိယာကို သန့်ရှင်းပြီး ထပ်စမ်းကြည့်ပါ"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"အာရုံခံကိရိယာကို သန့်ရှင်းပြီး ထပ်စမ်းကြည့်ပါ"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"အာရုံခံကိရိယာပေါ်တွင် သေချာဖိပါ"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"လက်ညှိုးအလွန်နှေးကွေးစွာ ရွေ့ခဲ့သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"အခြားလက်ဗွေဖြင့် စမ်းကြည့်ပါ"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"အလွန် လင်းသည်"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"အကြိမ်တိုင်း သင့်လက်ချောင်း၏တည်နေရာကို အနည်းငယ်ပြောင်းပါ"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"လက်ဗွေကို မသိရှိပါ"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"အာရုံခံကိရိယာပေါ်တွင် သေချာဖိပါ"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ကျော်ရန်"</string>
     <string name="no_matches" msgid="6472699895759164599">"ထပ်တူမတွေ့ရှိပါ"</string>
     <string name="find_on_page" msgid="5400537367077438198">"စာမျက်နှာတွင်ရှာဖွေရန်"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{ကိုက်ညီမှု # ခု}other{{total} ခု အနက် # ခု}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ပြီးပါပြီ"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"မျှဝေထားသည့် သိုလှောင်ခန်းကို ဖျက်နေသည်…"</string>
     <string name="share" msgid="4157615043345227321">"မျှဝေရန်"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> သည် နောက်ခံတွင်ပွင့်နေပြီး ဘက်ထရီအားကုန်စေသည်။ ပြန်ကြည့်ရန် တို့ပါ။"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> သည် နောက်ခံတွင် အချိန်အတော်ကြာပွင့်နေသည်။ ပြန်ကြည့်ရန် တို့ပါ။"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ပွင့်နေသည့်အက်ပ်များ စစ်ဆေးရန်"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ကင်မရာကို ဤစက်မှ အသုံးမပြုနိုင်ပါ"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 234ead8..65223a8 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"åpne kalenderen din"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"sende og lese SMS-meldinger"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Filer og dokumenter"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"åpne filer og dokumenter på enheten din"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musikk og annen lyd"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"få tilgang til lydfiler på enheten"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Bilder og videoer"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Skriv inn skjermlåsen for å fortsette"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Et delvis fingeravtrykk er registrert"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengjør fingeravtrykkssensoren og prøv igjen"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengjør sensoren og prøv igjen"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Trykk godt på sensoren"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du flyttet fingeren for sakte. Prøv på nytt."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv et annet fingeravtrykk"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"For lyst"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Endre posisjonen til fingeren litt hver gang"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Gjenkjenner ikke fingeravtrykket"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Trykk godt på sensoren"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet er autentisert"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Hopp over"</string>
     <string name="no_matches" msgid="6472699895759164599">"Ingen treff"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Finn på side"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# treff}other{# av {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Ferdig"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Sletter delt lagring …"</string>
     <string name="share" msgid="4157615043345227321">"Del"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> kjører i bakgrunnen og bruker batteri. Trykk for å gjennomgå."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> kjører lenge i bakgrunnen. Trykk for å gjennomgå."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Sjekk aktive apper"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Du har ikke tilgang til kameraet fra denne enheten"</string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 77257b1..fe006ce 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"छोड्नुहोस्"</string>
     <string name="no_matches" msgid="6472699895759164599">"कुनै मिलेन"</string>
     <string name="find_on_page" msgid="5400537367077438198">"पृष्ठमा फेला पार्नुहोस्"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# वटा मिल्दोजुल्दो परिणाम}other{{total} मध्ये # वटा मिल्दाजुल्दा परिणाम}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"भयो"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"साझेदारी गरिएको भण्डारण मेट्दै…"</string>
     <string name="share" msgid="4157615043345227321">"सेयर गर्नुहोस्"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ब्याकग्राउन्डमा चलिरहेको हुनाले ब्याट्री खपत भइरहेको छ। तपाईं यसका सम्बन्धमा समीक्षा गर्न चाहनुहुन्छ भने ट्याप गर्नुहोस्।"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> लामो समयदेखि ब्याकग्राउन्डमा चलिरहेको छ। तपाईं यसका सम्बन्धमा समीक्षा गर्न चाहनुहुन्छ भने ट्याप गर्नुहोस्।"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"कुन कुन एप सक्रिय छ भन्ने कुरा जाँच्नुहोस्"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"यो डिभाइसमा भएको क्यामेरा प्रयोग गर्न सकिएन"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f85f5f6..ea11ae9 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"toegang krijgen tot je agenda"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Sms"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"sms\'jes verzenden en bekijken"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Bestanden en documenten"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"bestanden en documenten op je apparaat openen"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muziek en andere audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"toegang krijgen tot audiobestanden op je apparaat"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Foto\'s en video\'s"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Voer je schermvergrendeling in om door te gaan"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Gedeeltelijke vingerafdruk gedetecteerd"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Reinig de vingerafdruksensor en probeer het opnieuw"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Reinig de sensor en probeer het opnieuw"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Druk stevig op de sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probeer een andere vingerafdruk"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Te veel licht"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Verander de positie van je vinger steeds een beetje"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Vingerafdruk niet herkend"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Druk stevig op de sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gezicht geverifieerd"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Overslaan"</string>
     <string name="no_matches" msgid="6472699895759164599">"Geen overeenkomsten"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Zoeken op pagina"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# overeenkomst}other{# van {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Klaar"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Gedeelde opslag wissen…"</string>
     <string name="share" msgid="4157615043345227321">"Delen"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> wordt uitgevoerd op de achtergrond en verbruikt veel batterijlading. Tik om te bekijken."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> wordt al lange tijd uitgevoerd op de achtergrond. Tik om te bekijken."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Actieve apps checken"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Kan geen toegang krijgen tot de camera vanaf dit apparaat"</string>
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0a945e0..618be48 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ଛାଡ଼ିଦିଅନ୍ତୁ"</string>
     <string name="no_matches" msgid="6472699895759164599">"କୌଣସି ମେଳକ ନାହିଁ"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ପୃଷ୍ଠାରେ ଖୋଜନ୍ତୁ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{#ଟି ମେଳ}other{{total}ଟିରୁ #ଟି ମେଳ}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ହୋଇଗଲା"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"ସେୟାର୍‍ ହୋଇଥିବା ଷ୍ଟୋରେଜ୍‍ ଲିଭାଉଛି…"</string>
     <string name="share" msgid="4157615043345227321">"ସେୟାର୍‍"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ପୃଷ୍ଠପଟରେ ଚାଲୁଛି ଏବଂ ବ୍ୟାଟେରୀର ଚାର୍ଜ ସମାପ୍ତ ହେଉଛି। ସମୀକ୍ଷା କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ଦୀର୍ଘ ସମୟ ଧରି ପୃଷ୍ଠପଟରେ ଚାଲୁଛି। ସମୀକ୍ଷା କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ସକ୍ରିୟ ଆପଗୁଡ଼ିକୁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ଏହି ଡିଭାଇସରୁ କ୍ୟାମେରାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ"</string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 294dd3c..2c214b9 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਦੇਖੋ"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ਫ਼ਾਈਲਾਂ ਅਤੇ ਦਸਤਾਵੇਜ਼"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਫ਼ਾਈਲਾਂ ਅਤੇ ਦਸਤਾਵੇਜ਼ਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"ਸੰਗੀਤ ਅਤੇ ਹੋਰ ਆਡੀਓ"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਆਡੀਓ ਫ਼ਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਸਕ੍ਰੀਨ ਲਾਕ ਦਾਖਲ ਕਰੋ"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"ਅੰਸ਼ਕ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਹੋ ਸਕੀ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ਸੈਂਸਰ ਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ਸੈਂਸਰ ਨੂੰ ਜ਼ੋਰ ਨਾਲ ਦਬਾਓ"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ਉਂਗਲ ਕਾਫ਼ੀ ਹੌਲੀ ਮੂਵ ਹੋਈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ਕੋਈ ਹੋਰ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ਹਰ ਵਾਰ ਆਪਣੀ ਉਂਗਲ ਨੂੰ ਥੋੜ੍ਹਾ ਹਿਲਾਓ"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"ਸੈਂਸਰ ਨੂੰ ਜ਼ੋਰ ਨਾਲ ਦਬਾਓ"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ਛੱਡੋ"</string>
     <string name="no_matches" msgid="6472699895759164599">"ਕੋਈ ਮੇਲ ਨਹੀਂ"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ਸਫ਼ੇ ਤੇ ਲੱਭੋ"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# ਮਿਲਾਨ}one{{total} ਵਿੱਚੋਂ #}other{{total} ਵਿੱਚੋਂ #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ਹੋ ਗਿਆ"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"ਸਾਂਝੀ ਕੀਤੀ ਸਟੋਰੇਜ ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ…"</string>
     <string name="share" msgid="4157615043345227321">"ਸਾਂਝਾ ਕਰੋ"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀ ਹੈ ਅਤੇ ਬੈਟਰੀ ਦੀ ਖਪਤ ਕਰ ਰਹੀ ਹੈ। ਸਮੀਖਿਆ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ਲੰਮੇ ਸਮੇਂ ਤੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀ ਹੈ। ਸਮੀਖਿਆ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ਕਿਰਿਆਸ਼ੀਲ ਐਪਾਂ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ਇਸ ਡੀਵਾਈਸ ਤੋਂ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index a316127..1955561 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"dostęp do kalendarza"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"wysyłanie i wyświetlanie SMS‑ów"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Pliki i dokumenty"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"dostęp do plików i dokumentów na urządzeniu"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muzyka i inne dźwięki"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"dostęp do plików audio na urządzeniu"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Zdjęcia i filmy"</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Użyj blokady ekranu, aby kontynuować"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Wykryto częściowy odcisk palca"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Wyczyść czytnik linii papilarnych i spróbuj ponownie"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Wyczyść czujnik i spróbuj ponownie"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czujnik"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Użyj odcisku innego palca"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zbyt jasno"</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Za każdym razem lekko zmieniaj ułożenie palca"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Nie rozpoznano odcisku palca"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Mocno naciśnij czujnik"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Twarz rozpoznana"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Pomiń"</string>
     <string name="no_matches" msgid="6472699895759164599">"Brak wyników"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Znajdź na stronie"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# dopasowanie}few{# z {total}}many{# z {total}}other{# z {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Gotowe"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Kasuję dane z pamięci współdzielonej…"</string>
     <string name="share" msgid="4157615043345227321">"Udostępnij"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> działa w tle i zużywa baterię. Kliknij, aby sprawdzić."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> długo działa w tle. Kliknij, aby sprawdzić."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Sprawdź aktywne aplikacje"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nie można uzyskać dostępu do aparatu z tego urządzenia"</string>
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index fd01b22..2092f7e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acesse sua agenda"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"envie e veja mensagens SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Arquivos e documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"acessar arquivos e documentos no seu dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música e outros áudios"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"acessar arquivos de áudio no seu dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos e vídeos"</string>
@@ -539,16 +537,16 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite que o app acesse a configuração do Bluetooth no tablet, além de fazer e aceitar conexões com dispositivos pareados."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite que o app acesse a configuração do Bluetooth no dispositivo Android TV, além de fazer e aceitar conexões com dispositivos pareados."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite que o app acesse a configuração do Bluetooth no telefone, além de fazer e aceitar conexões com dispositivos pareados."</string>
-    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"descobrir e se parear a disp. Bluetooth por perto"</string>
+    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"descobrir e parear com disp. Bluetooth por perto"</string>
     <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite que o app descubra e se pareie a dispositivos Bluetooth por perto"</string>
-    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"conecte-se a dispositivos Bluetooth pareados"</string>
+    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"conectar a dispositivos Bluetooth pareados"</string>
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Permite que o app se conecte a dispositivos Bluetooth pareados"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"anunciar em dispositivos Bluetooth por perto"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que o app seja anunciado em dispositivos Bluetooth por perto"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app veicule anúncios, conecte-se à rede e determine a posição relativa de dispositivos Wi-Fi por perto"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Impressão digital parcial detectada"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O movimento do dedo está muito lento. Tente novamente."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Pressione o sensor com firmeza"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
     <string name="no_matches" msgid="6472699895759164599">"Não encontrado"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Limpando armazenamento compartilhado…"</string>
     <string name="share" msgid="4157615043345227321">"Compartilhar"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> está sendo executado em segundo plano e drenando a energia da bateria. Toque para revisar."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> está sendo executado em segundo plano faz muito tempo. Toque para revisar."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificar apps ativos"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Não é possível acessar a câmera neste dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index aa8571b..31cf3d5 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"aceder ao calendário"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"enviar e ver mensagens SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Ficheiros e documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"aceder a ficheiros e documentos no seu dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música e outro áudio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"aceder a ficheiros de áudio no dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos e vídeos"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduza o bloqueio de ecrã para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Impressão digital parcial detetada"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressões digitais e tente novamente"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prima firmemente o sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Moveu o dedo demasiado lentamente. Tente novamente."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Experimente outra impressão digital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Está demasiado claro"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Altere a posição do seu dedo ligeiramente de cada vez"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Prima firmemente o sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado."</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Ignorar"</string>
     <string name="no_matches" msgid="6472699895759164599">"Sem correspondências"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"A apagar o armazenamento partilhado…"</string>
     <string name="share" msgid="4157615043345227321">"Partilhar"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"A app <xliff:g id="APP">%1$s</xliff:g> está a ser executada em segundo plano e a consumir rapidamente a bateria Toque para analisar."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"A app <xliff:g id="APP">%1$s</xliff:g> está a ser executada em segundo plano há muito tempo. Toque para analisar."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificar apps ativas"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Não é possível aceder à câmara a partir deste dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index fd01b22..2092f7e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"acesse sua agenda"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"envie e veja mensagens SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Arquivos e documentos"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"acessar arquivos e documentos no seu dispositivo"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Música e outros áudios"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"acessar arquivos de áudio no seu dispositivo"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotos e vídeos"</string>
@@ -539,16 +537,16 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite que o app acesse a configuração do Bluetooth no tablet, além de fazer e aceitar conexões com dispositivos pareados."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite que o app acesse a configuração do Bluetooth no dispositivo Android TV, além de fazer e aceitar conexões com dispositivos pareados."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite que o app acesse a configuração do Bluetooth no telefone, além de fazer e aceitar conexões com dispositivos pareados."</string>
-    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"descobrir e se parear a disp. Bluetooth por perto"</string>
+    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"descobrir e parear com disp. Bluetooth por perto"</string>
     <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite que o app descubra e se pareie a dispositivos Bluetooth por perto"</string>
-    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"conecte-se a dispositivos Bluetooth pareados"</string>
+    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"conectar a dispositivos Bluetooth pareados"</string>
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Permite que o app se conecte a dispositivos Bluetooth pareados"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"anunciar em dispositivos Bluetooth por perto"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que o app seja anunciado em dispositivos Bluetooth por perto"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app veicule anúncios, conecte-se à rede e determine a posição relativa de dispositivos Wi-Fi por perto"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Impressão digital parcial detectada"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O movimento do dedo está muito lento. Tente novamente."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Impressão digital não reconhecida"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Pressione o sensor com firmeza"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
     <string name="no_matches" msgid="6472699895759164599">"Não encontrado"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Localizar na página"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# correspondência}one{# de {total}}other{# de {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Concluído"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Limpando armazenamento compartilhado…"</string>
     <string name="share" msgid="4157615043345227321">"Compartilhar"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> está sendo executado em segundo plano e drenando a energia da bateria. Toque para revisar."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> está sendo executado em segundo plano faz muito tempo. Toque para revisar."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificar apps ativos"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Não é possível acessar a câmera neste dispositivo"</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index fab2656..d9c7e3a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1502,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Omiteți"</string>
     <string name="no_matches" msgid="6472699895759164599">"Nicio potrivire"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Găsiți pe pagină"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# potrivire}few{# din {total}}other{# din {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Terminat"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Se șterge spațiul de stocare distribuit..."</string>
     <string name="share" msgid="4157615043345227321">"Distribuiți"</string>
@@ -2257,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal și consumă bateria. Atingeți pentru a examina."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal mult timp. Atingeți pentru a examina."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificați aplicațiile active"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nu se poate accesa camera foto de pe acest dispozitiv"</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0ba7cb0..5343704 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"доступ к календарю"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"отправлять и просматривать SMS-сообщения"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файлы и документы"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"Доступ к файлам и документам на вашем устройстве"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музыка и другие аудиозаписи"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"доступ к аудиофайлам на вашем устройстве"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Фото и видео"</string>
@@ -343,7 +341,7 @@
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Регистрировать жесты на сканере отпечатков пальцев"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Использовать сканер отпечатков пальцев для дополнительных жестов."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Создавать скриншоты"</string>
-    <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Создавать скриншоты экрана."</string>
+    <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Создавать снимки экрана."</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"Отключение/изменение строки состояния"</string>
     <string name="permdesc_statusBar" msgid="5809162768651019642">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string>
     <string name="permlab_statusBarService" msgid="2523421018081437981">"Замена строки состояния"</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Чтобы продолжить, разблокируйте экран."</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Отсканирована только часть отпечатка."</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не удалось распознать отпечаток. Повторите попытку."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистите сканер отпечатков пальцев и повторите попытку."</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистите сканер и повторите попытку."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру."</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Вы перемещали палец слишком медленно. Повторите попытку."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Попробуйте сохранить отпечаток другого пальца."</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Слишком светло."</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Каждый раз немного меняйте положение пальца."</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечаток не распознан."</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Плотно прижмите палец к сканеру."</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Пропустить"</string>
     <string name="no_matches" msgid="6472699895759164599">"Нет совпадений"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Найти на странице"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# совпадение}one{# совпадение из {total}}few{# совпадения из {total}}many{# совпадений из {total}}other{# совпадения из {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Очистка единого хранилища…"</string>
     <string name="share" msgid="4157615043345227321">"Поделиться"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Приложение \"<xliff:g id="APP">%1$s</xliff:g>\" работает в фоновом режиме и расходует заряд батареи. Нажмите, чтобы узнать подробности."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Приложение \"<xliff:g id="APP">%1$s</xliff:g>\" работает в фоновом режиме уже длительное время. Нажмите, чтобы узнать подробности."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверить активные приложения"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Камера на этом устройстве недоступна."</string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 2c9950c..b72a115 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"කෙටි පණිවිඩ"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS පණිවිඩ යැවීම සහ බැලීම"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ගොනු සහ ලේඛන"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"ඔබගේ උපාංගයේ ගොනු සහ ලේඛන වෙත ප්‍රවේශ වන්න"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"සංගීතය සහ වෙනත් ශ්‍රව්‍ය"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"ඔබගේ උපාංගයෙහි ඇති ශ්‍රව්‍ය ගොනුවලට ප්‍රවේශ වන්න"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"ඡායාරූප සහ වීඩියෝ"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ඉදිරියට යාමට ඔබගේ තිර අගුල ඇතුළත් කරන්න"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"අර්ධ ඇඟිලි සලකුණක් අනාවරණය කරන ලදි"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ඇඟිලි සලකුණු සංවේදකය පිරිසිදු කර නැවත උත්සාහ කරන්න"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"සංවේදකය පිරිසිදු කර නැවත උත්සාහ කරන්න"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"සංවේදකය මත තදින් ඔබන්න"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"තවත් ඇඟිලි සලකුණක් උත්සාහ කරන්න"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"දීප්තිය වැඩියි"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"එක් එක් අවස්ථාවේ ඔබගේ ඇඟිල්ලේ පිහිටීම මදක් වෙනස් කරන්න"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ඇඟිලි සලකුණ හඳුනා නොගන්නා ලදි"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"සංවේදකය මත තදින් ඔබන්න"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්‍යාපනය කරන ලදී"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"මුහුණ සත්‍යාපනය කරන ලදී"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්‍යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"මඟ හරින්න"</string>
     <string name="no_matches" msgid="6472699895759164599">"ගැලපීම් නැත"</string>
     <string name="find_on_page" msgid="5400537367077438198">"පිටුවෙහි සෙවීම"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{ගැළපීම් #}one{{total}කින් #}other{{total}කින් #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"හරි"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"බෙදා ගත් ගබඩාව මකා දමමින්…"</string>
     <string name="share" msgid="4157615043345227321">"බෙදාගන්න"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> පසුබිමේ ධාවනය වන අතර බැටරිය බැස යයි. සමාලෝචනය කිරීමට තට්ටු කරන්න."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> දිගු වේලාවක් පසුබිමේ ධාවනය වේ. සමාලෝචනය කිරීමට තට්ටු කරන්න."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"සක්‍රිය යෙදුම් පරීක්ෂා කරන්න"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"මෙම උපාංගයෙන් කැමරාවට ප්‍රවේශ විය නොහැකිය"</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ce70e5f..d46908a 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1503,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Preskočiť"</string>
     <string name="no_matches" msgid="6472699895759164599">"Žiadne zhody"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Vyhľadať na stránke"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# zhoda}few{# z {total}}many{# of {total}}other{# z {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Hotovo"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Vymazáva sa zdieľané úložisko…"</string>
     <string name="share" msgid="4157615043345227321">"Zdieľať"</string>
@@ -2258,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikácie <xliff:g id="APP">%1$s</xliff:g> je spustená na pozadí a vybíja batériu. Skontrolujte to klepnutím."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikácia <xliff:g id="APP">%1$s</xliff:g> je dlhodobo spustená na pozadí. Skontrolujte to klepnutím."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Skontrolovať aktívne aplikácie"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"V tomto zariadení nemáte prístup ku kamere"</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index e23cf85..44498f4 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -547,7 +547,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Aplikaciji dovoljuje oddajanje napravam Bluetooth v bližini."</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"določanje relativne oddaljenosti med napravami UWB v bližini"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikaciji dovoli, da določi relativno oddaljenost med napravami UWB v bližini."</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"komunikacijo z napravami Wi‑Fi v bližini"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"komunikacija z napravami Wi‑Fi v bližini"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji dovoljuje objavljanje in določanje relativnega položaja naprav Wi‑Fi v bližini ter povezovanje z njimi."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikaciji omogoča pridobivanje podatkov o prednostni storitvi za plačevanje prek povezave NFC, kot so registrirani pripomočki in cilj preusmeritve."</string>
@@ -1503,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
     <string name="no_matches" msgid="6472699895759164599">"Ni ujemanj"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Najdi na strani"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# ujemanje}one{# od {total}}two{# od {total}}few{# od {total}}other{# od {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Končano"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Brisanje skupne shrambe …"</string>
     <string name="share" msgid="4157615043345227321">"Deli"</string>
@@ -2258,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> se izvaja v ozadju in porablja energijo baterije. Dotaknite se za pregled."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> se dolgo časa izvaja v ozadju. Dotaknite se za pregled."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Preverite aktivne aplikacije"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"V tej napravi ni mogoče dostopati do fotoaparata."</string>
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index ec07f41..0092303 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"qasje te kalendari yt"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"dërgo dhe shiko mesazhet SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Skedarët dhe dokumentet"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"përfito qasje te skedarët dhe dokumentet në pajisjen tënde"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muzikë dhe audio të tjera"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"të ketë qasje te skedarët audio në pajisjen tënde"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotografitë dhe videot"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Lejo që aplikacioni të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"të ndërveprojë me pajisjet Wi-Fi në afërsi"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lejon që aplikacioni të reklamojë, të lidhet dhe të përcaktojë pozicionin relativ të pajisjeve Wi-Fi në afërsi"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lejon që aplikacioni të reklamojë, të lidhet dhe të përcaktojë pozicionin përkatës të pajisjeve Wi-Fi në afërsi"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacionet për shërbimin e preferuar të pagesës me NFC"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lejon aplikacionin të marrë informacione për shërbimin e preferuar të pagesës me NFC si p.sh. ndihmat e regjistruara dhe destinacionin e itinerarit."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"kontrollo \"Komunikimin e fushës në afërsi\" NFC"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Fut kyçjen e ekranit për të vazhduar"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"U zbulua gjurmë gishti e pjesshme"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Pastro sensorin e gjurmës së gishtit dhe provo sërish"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Pastro sensorin dhe provo sërish"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Shtyp fort te sensori"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Gishti lëvizi shumë ngadalë. Provo përsëri."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Provo një gjurmë gishti tjetër"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Me shumë ndriçim"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ndrysho pak pozicionin e gishtit çdo herë"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Gjurma e gishtit nuk u njoh"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Shtyp fort te sensori"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Fytyra u vërtetua"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Kapërce"</string>
     <string name="no_matches" msgid="6472699895759164599">"Asnjë përputhje"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Gjej brenda faqes"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# përputhje}other{# nga {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"U krye"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Po fshin hapësirën ruajtëse të brendshme…"</string>
     <string name="share" msgid="4157615043345227321">"Shpërndaj"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> po ekzekutohet në sfond dhe po shkarkon baterinë. Trokit për ta shqyrtuar."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> po ekzekutohet në sfond për një kohe të gjatë. Trokit për ta shqyrtuar."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Kontrollo aplikacionet aktive"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Nuk mund të qasesh te kamera nga kjo pajisje"</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index caac95b..3f286a3 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -305,10 +305,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"приступи календару"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"шаље и прегледа SMS поруке"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Фајлови и документи"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"приступање фајловима и документима на уређају"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музика и други аудио садржај"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"приступ аудио фајловима на уређају"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Слике и видео снимци"</string>
@@ -589,12 +587,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Употребите закључавање екрана да бисте наставили"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Откривен је делимичан отисак прста"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Није успела обрада отиска прста. Пробајте поново."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Обришите сензор за отисак прста и пробајте поново"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Обришите сензор и пробајте поново"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Јако притисните сензор"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Превише споро сте померили прст. Пробајте поново."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте са другим отиском прста"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Превише је светло"</string>
@@ -602,10 +597,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Сваки пут лагано промените положај прста"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отисак прста није препознат"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Јако притисните сензор"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string>
@@ -1509,8 +1502,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Прескочи"</string>
     <string name="no_matches" msgid="6472699895759164599">"Нема подударања"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Пронађи на страници"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# подударање}one{# од {total}}few{# од {total}}other{# од {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Брише се дељени меморијски простор…"</string>
     <string name="share" msgid="4157615043345227321">"Дели"</string>
@@ -2096,7 +2088,7 @@
     <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Обавештења"</string>
     <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Брза подешавања"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Дијалог напајања"</string>
-    <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Закључани екран"</string>
+    <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Закључавање екрана"</string>
     <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Снимак екрана"</string>
     <string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"Кука за слушалице"</string>
     <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Пречица за приступачност на екрану"</string>
@@ -2264,6 +2256,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута у позадини и троши батерију. Додирните да бисте прегледали."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Апликација <xliff:g id="APP">%1$s</xliff:g> је предуго покренута у позадини. Додирните да бисте прегледали."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Проверите активне апликације"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Не можете да приступите камери са овог уређаја"</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4adcc47..db91aaa 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"få tillgång till din kalender"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Sms"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"skicka och visa sms"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Filer och dokument"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"visa filer och dokument på enheten"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musik och övrigt ljud"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"komma åt ljudfiler på din enhet"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Foton och videor"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Fortsätt med hjälp av ditt skärmlås"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Ofullständigt fingeravtryck upptäcktes"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengör fingeravtryckssensorn och försök igen"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengör sensorn och försök igen"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tryck på sensorn med ett stadigt tryck"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du rörde fingret för långsamt. Försök igen."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Testa ett annat fingeravtryck"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Det är för ljust"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flytta fingret lite varje gång"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Fingeravtrycket känns inte igen"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Tryck på sensorn med ett stadigt tryck"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Hoppa över"</string>
     <string name="no_matches" msgid="6472699895759164599">"Inga träffar"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Sök på sidan"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# resultat}other{# av {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Klar"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Delat lagringsutrymme rensas …"</string>
     <string name="share" msgid="4157615043345227321">"Dela"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> körs i bakgrunden så att batteriet tar slut fortare. Tryck för att granska."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> har körts i bakgrunden under lång tid. Tryck för att granska."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Kontrollera aktiva appar"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Det går inte att komma åt kameran från den här enheten"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 02572b0..b84a85f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"ifikie kalenda yako"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"itume na iangalie SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Faili na hati"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"fikia faili na hati kwenye kifaa chako"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Muziki na sauti nyingine"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"fikia faili za sauti kwenye kifaa chako"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Picha na video"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Weka mbinu yako ya kufunga skrini ili uendelee"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Kimetambua sehemu ya alama ya kidole"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Imeshindwa kuchakata alama ya kidole. Tafadhali jaribu tena."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Safisha kitambua alama ya kidole kisha ujaribu tena"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Safisha kitambuzi kisha ujaribu tena"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Bonyeza kwa nguvu kwenye kitambuzi"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Kidole kilisogezwa polepole zaidi. Tafadhali jaribu tena."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Jaribu alama nyingine ya kidole"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Inang\'aa mno"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Badilisha mkao wa kidole chako kiasi kila wakati"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Alama ya kidole haijatambuliwa"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Bonyeza kwa nguvu kwenye kitambuzi"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Uso umethibitishwa"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Ruka"</string>
     <string name="no_matches" msgid="6472699895759164599">"Hakuna vinavyolingana"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Pata kwenye ukurasa"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# inayolingana}other{# kati ya {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Nimemaliza"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Inafuta hifadhi iliyoshirikiwa…"</string>
     <string name="share" msgid="4157615043345227321">"Shiriki"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> inatumika chinichini na kumaliza nishati ya betri. Gusa ili ukague."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> inatumika chinichini kwa muda mrefu. Gusa ili ukague."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Angalia programu zinazotumika"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Haiwezi kufikia kamera kwenye kifaa hiki"</string>
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9acbac6..d312d95 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -545,7 +545,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"அருகிலுள்ள புளூடூத் சாதனங்களுக்குத் தெரியப்படுத்த ஆப்ஸை அனுமதிக்கும்"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானித்தல்"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானிக்க ஆப்ஸை அனுமதிக்கும்"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"அருகிலுள்ள வைஃபை சாதனங்களுடன் தொடர்பில் இருக்கும்"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"அருகிலுள்ள வைஃபை சாதனங்களுடன் தொடர்பு கொள்ளுதல்"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"அருகிலுள்ள வைஃபை சாதனங்களைத் தெரியப்படுத்தவும் இணைக்கவும் இருப்பிடத்தைத் தீர்மானிக்கவும் இது ஆப்ஸை அனுமதிக்கும்"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்கள்"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"பதிவுசெய்யப்பட்ட கருவிகள், சேருமிடத்திற்கான வழி போன்ற விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்களைப் பெற ஆப்ஸை அனுமதிக்கிறது."</string>
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"தவிர்"</string>
     <string name="no_matches" msgid="6472699895759164599">"பொருத்தம் ஏதுமில்லை"</string>
     <string name="find_on_page" msgid="5400537367077438198">"பக்கத்தில் கண்டறி"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# பொருத்தம்}other{# / {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"முடிந்தது"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"பகிர்ந்த சேமிப்பகத்தை அழிக்கிறது…"</string>
     <string name="share" msgid="4157615043345227321">"பகிர்"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> ஆப்ஸ் பின்னணியில் இயங்குவதுடன் பேட்டரியை அதிகமாகப் பயன்படுத்துகிறது. பார்க்க தட்டவும்."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ஆப்ஸ் நீண்ட நேரமாகப் பின்னணியில் இயங்குகிறது. பார்க்க தட்டவும்."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"செயலிலுள்ள ஆப்ஸைப் பாருங்கள்"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"இந்தச் சாதனத்திலிருந்து கேமராவை அணுக முடியவில்லை"</string>
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 6e4ef59..6e7f980 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"దాటవేయి"</string>
     <string name="no_matches" msgid="6472699895759164599">"సరిపోలికలు లేవు"</string>
     <string name="find_on_page" msgid="5400537367077438198">"పేజీలో కనుగొనండి"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# మ్యాచ్}other{#లో {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"పూర్తయింది"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"షేర్ చేసిన నిల్వను తొలగిస్తోంది…"</string>
     <string name="share" msgid="4157615043345227321">"షేర్"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> బ్యాక్‌గ్రౌండ్‌లో రన్ అవుతోంది, బ్యాటరీని ఎక్కువగా వాడుతోంది. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> చాలా సమయం నుండి బ్యాక్‌గ్రౌండ్‌లో రన్ అవుతోంది. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"యాక్టివ్‌గా ఉన్న యాప్‌లను చెక్ చేయండి"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"ఈ పరికరం నుండి కెమెరాను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
 </resources>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 009df4a..0db08fb 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -45,6 +45,10 @@
     <item name="config_pictureInPictureExpandedVerticalWidth"
           format="dimension" type="dimen">110dp</item>
 
+    <!-- The behavior when an activity has not specified a preference to dock big overlays or not.
+         Docking puts the activity side-by-side next to the big overlay windows. -->
+    <bool name="config_dockBigOverlayWindows">true</bool>
+
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">false</bool>
 
@@ -57,6 +61,12 @@
         com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
     </string>
 
+    <!-- Component name of the activity used to inform a user about a sensory being blocked because
+     of hardware privacy switches. -->
+    <string name="config_sensorUseStartedActivity_hwToggle" translatable="false">
+        com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
+    </string>
+
     <!-- Component name of the activity that shows the request for access to a usb device. -->
     <string name="config_usbPermissionActivity" translatable="false">
         com.android.systemui/com.android.systemui.usb.tv.TvUsbPermissionActivity
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4b33e95..2e987ac 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"เข้าถึงปฏิทิน"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"ส่งและดูข้อความ SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"ไฟล์และเอกสาร"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"เข้าถึงไฟล์และเอกสารในอุปกรณ์"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"เพลงและเสียงอื่นๆ"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"เข้าถึงไฟล์เสียงในอุปกรณ์"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"รูปภาพและวิดีโอ"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ป้อนข้อมูลการล็อกหน้าจอเพื่อดำเนินการต่อ"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"ตรวจพบลายนิ้วมือบางส่วน"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ทำความสะอาดเซ็นเซอร์ลายนิ้วมือแล้วลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ทำความสะอาดเซ็นเซอร์แล้วลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"กดเซ็นเซอร์ให้แน่น"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"นิ้วเคลื่อนที่ช้าเกินไป โปรดลองอีกครั้ง"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ลองลายนิ้วมืออื่น"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"สว่างเกินไป"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"เปลี่ยนตำแหน่งของนิ้วเล็กน้อยไปเรื่อยๆ ทุกครั้ง"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ไม่รู้จักลายนิ้วมือ"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"กดเซ็นเซอร์ให้แน่น"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"ข้าม"</string>
     <string name="no_matches" msgid="6472699895759164599">"ไม่พบรายการที่ตรงกัน"</string>
     <string name="find_on_page" msgid="5400537367077438198">"ค้นหาบนหน้า"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{ตรงกัน # รายการ}other{ตรงกัน # รายการจาก {total} รายการ}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"เสร็จสิ้น"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"กำลังลบพื้นที่เก็บข้อมูลที่แชร์…"</string>
     <string name="share" msgid="4157615043345227321">"แชร์"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงานอยู่ในเบื้องหลังและทำให้เปลืองแบตเตอรี่ แตะเพื่อตรวจสอบ"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> ทำงานอยู่ในเบื้องหลังเป็นเวลานาน แตะเพื่อตรวจสอบ"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"ตรวจสอบแอปที่ใช้งานอยู่"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"เข้าถึงกล้องจากอุปกรณ์นี้ไม่ได้"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e443676..e455689 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"i-access ang iyong kalendaryo"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"magpadala at tumingin ng mga mensaheng SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Mga file at dokumento"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"i-access ang mga file at dokumento sa iyong device"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musika at iba pang audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"mag-access ng mga audio file sa iyong device"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Mga larawan at video"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ilagay ang iyong lock ng screen para magpatuloy"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Hindi buo ang natukoy na fingerprint"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Hindi maproseso ang fingerprint. Pakisubukan ulit."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Linisin ang sensor para sa fingerprint at subukan ulit"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Linisin ang sensor at subukan ulit"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pumindot nang madiin sa sensor"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukan ulit."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Sumubok ng ibang fingerprint"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Masyadong maliwanag"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Bahagyang baguhin ang posisyon ng iyong daliri sa bawat pagkakataon"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Hindi nakilala ang fingerprint"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Pumindot nang madiin sa sensor"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Na-authenticate ang mukha"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Laktawan"</string>
     <string name="no_matches" msgid="6472699895759164599">"Walang mga tugma"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Maghanap sa pahina"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# tugma}one{# sa {total}}other{# sa {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Tapos na"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Binubura ang nakabahaging storage…"</string>
     <string name="share" msgid="4157615043345227321">"Ibahagi"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Gumagana ang <xliff:g id="APP">%1$s</xliff:g> sa background at gumagamit ito ng baterya I-tap para suriin."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Napakatagal nang gumagana ang <xliff:g id="APP">%1$s</xliff:g> sa background. I-tap para suriin."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Tingnan ang mga aktibong app"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Hindi ma-access ang camera mula sa device na ito"</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9546de4..42072aa 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"takviminize erişme"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS mesajları gönderme ve görüntüleme"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Dosyalar ve dokümanlar"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"cihazınızdaki dosyalara ve dokümanlara erişme"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Müzik ve diğer sesler"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"cihazınızdaki ses dosyalarına erişme"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Fotoğraflar ve videolar"</string>
@@ -547,7 +545,7 @@
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Uygulamaya, yakındaki Bluetooth cihazlara reklam yayınlama izni verir"</string>
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu bul"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Uygulamanın, yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu belirlemesine izin verin"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yakındaki kablosuz cihazlarla etkileşim kurma"</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yakındaki kablosuz cihazlarla etkileşim kur"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Uygulamanın reklam sunmasına, bağlanmasına ve yakındaki kablosuz cihazların göreli konumunu belirlemesine izin verir"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tercih Edilen NFC Ödeme Hizmeti Bilgileri"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Uygulamaya, kayıtlı yardımlar ve rota hedefi gibi tercih edilen NFC ödeme hizmeti bilgilerini alma izni verir."</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Devam etmek için ekran kilidinizi girin"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Parmak izinin tümü algılanamadı"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Parmak izi sensörünü temizleyip tekrar deneyin"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensörü temizleyip tekrar deneyin"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensöre sıkıca bastırın"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başka bir parmak izi deneyin"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çok parlak"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Her defasında parmağınızın konumunu biraz değiştirin"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Parmak izi tanınmadı"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Sensöre sıkıca bastırın"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yüz kimliği doğrulandı"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Atla"</string>
     <string name="no_matches" msgid="6472699895759164599">"Eşleşme yok"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Sayfada bul"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# eşleştirme}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Bitti"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Paylaşılan depolama alanı siliniyor…"</string>
     <string name="share" msgid="4157615043345227321">"Paylaş"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> arka planda çalışıyor ve pil tüketiyor. İncelemek için dokunun."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> uzun süredir arka planda çalışıyor. İncelemek için dokunun."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Etkin uygulamaları kontrol edin"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Bu cihazdan kameraya erişilemiyor"</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cc763ad..edd87b0 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -306,10 +306,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"отримувати доступ до календаря"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"надсилати та переглядати SMS-повідомлення"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Файли та документи"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"отримувати доступ до файлів і документів на вашому пристрої"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Музика й інше аудіо"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"отримувати доступ до аудіофайлів на вашому пристрої"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Фото й відео"</string>
@@ -590,12 +588,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Щоб продовжити, введіть дані для розблокування екрана"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Виявлено частковий відбиток пальця"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистьте сканер відбитків пальців і повторіть спробу"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистьте сканер і повторіть спробу"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Міцно притисніть палець до сканера"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ви провели пальцем надто повільно. Повторіть спробу."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Спробуйте інший відбиток пальця"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Надто яскраво"</string>
@@ -603,10 +598,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Щоразу трохи змінюйте положення пальця"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Відбиток пальця не розпізнано"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Міцно притисніть палець до сканера"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Обличчя автентифіковано"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
@@ -1510,8 +1503,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Пропустити"</string>
     <string name="no_matches" msgid="6472699895759164599">"Немає збігів"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Знайти на сторінці"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# збіг}one{# з {total}}few{# з {total}}many{# з {total}}other{# з {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Готово"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Стирання спільної пам’яті…"</string>
     <string name="share" msgid="4157615043345227321">"Надіслати"</string>
@@ -2265,6 +2257,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"Додаток <xliff:g id="APP">%1$s</xliff:g> працює у фоновому режимі та розряджає акумулятор. Натисніть, щоб переглянути."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"Додаток <xliff:g id="APP">%1$s</xliff:g> довго працює у фоновому режимі. Натисніть, щоб переглянути."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Перевірте активні додатки"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Не вдається отримати доступ до камери через цей пристрій"</string>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8636572..91c8be7cb 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"نظر انداز کریں"</string>
     <string name="no_matches" msgid="6472699895759164599">"کوئی مماثلتیں نہیں ہیں"</string>
     <string name="find_on_page" msgid="5400537367077438198">"صفحہ پر تلاش کریں"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# مماثلت}other{{total} میں سے #}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"ہو گیا"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"اشتراک کردہ اسٹوریج کو صاف کیا جا رہا ہے…"</string>
     <string name="share" msgid="4157615043345227321">"اشتراک کریں"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> پس منظر میں چل رہی ہے اور بیٹری ختم ہو رہی ہے۔ جائزے کے لیے تھپتھپائیں۔"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> کافی وقت سے پس منظر میں چل رہی ہے۔ جائزے کے لیے تھپتھپائیں۔"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"فعال ایپس چیک کریں"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"اس آلہ سے کیمرا تک رسائی حاصل نہیں کر سکتے"</string>
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index e09f634..b82999f 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"taqvimingizga kirish"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS xabarlarni yuborish va ko‘rish"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Fayl va hujjatlar"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"qurilmangizdagi fayl va hujjatlarga kirish"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Musiqa va boshqa audio"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"qurilmangizdagi audio fayllarga kirish"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Suratlar va videolar"</string>
@@ -539,16 +537,16 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Ilovaga planshetdagi Bluetooth‘ning sozlamasini ko‘rishga va bog‘langan qurilmalarga ulanish va ulardan ulanish so‘rovlarini qabul qulishga imkon beradi."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Ilovaga Android TV qurilmangizdagi Bluetooth sozlamasini koʻrishga va bogʻlangan qurilmalarga ulanish va ulardan ulanish talablarni qabul qilishga imkon beradi."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Ilovaga telefondagi Bluetooth‘ning sozlamasini ko‘rishga va bog‘langan qurilmalarga ulanish va ulardan ulanish so‘rovlarini qabul qulishga imkon beradi."</string>
-    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Bluetooth qurilmalarini topish va juftlashish"</string>
+    <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Bluetooth qurilmalarni topish va juftlash"</string>
     <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Ilovaga yaqin-atrofdagi Bluetooth qurilmalarini topish va juftlashish uchun ruxsat beradi"</string>
-    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"juftlangan Bluetooth qurilmalariga ulanish"</string>
+    <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Juftlangan Bluetooth qurilmalarga ulanish"</string>
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Ilovaga juftlangan Bluetooth qurilmalariga ulanish uchun ruxsat beradi"</string>
-    <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"atrofdagi Bluetooth qurilmalariga reklama berish"</string>
+    <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Atrofdagi Bluetooth qurilmalarga reklama yuborish"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Ilovaga yaqin-atrofdagi Bluetooth qurilmalariga reklama yuborish imkonini beradi"</string>
-    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlash"</string>
+    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Atrofdagi ultra-keng aloqa kanalli qurilmalarning nisbiy joylashuvini aniqlash"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ilovaga yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlashga ruxsat beradi"</string>
-    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Yaqin-atrofdagi Wi-Fi qurilmalari bilan ishlash"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ilovaga yaqin-atrofdagi Wi-Fi qurilmalarga reklama yuborish, ulanish va taxminiy joylashuvini aniqlash imkonini beradi."</string>
+    <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Yaqin-atrofdagi Wi-Fi qurilmalar bilan ishlash"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ilovaga yaqin-atrofdagi Wi-Fi qurilmalarga reklama yuborish, ulanish va ularning taxminiy joylashuvini aniqlash imkonini beradi."</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Asosiy NFC toʻlov xizmati haqidagi axborot"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Bu ilovaga asosiy NFC toʻlov xizmati haqidagi axborotni olish imkonini beradi (masalan, qayd qilingan AID identifikatorlari va marshrutning yakuniy manzili)."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"NFC modulini boshqarish"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ekran qulfini kiritish bilan davom eting"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Barmoq izi qismi aniqlandi"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Barmoq izi aniqlanmadi. Qaytadan urining."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Barmoq izi skanerini tozalang va qayta urining"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensorni tozalang va qayta urining"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensorni mahkam bosing"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Boshqa barmoq izi bilan urining"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Juda yorqin"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Barmoqni har safar biroz surib joylang"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Barmoq izi aniqlanmadi"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Sensorni mahkam bosing"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yuzingiz aniqlandi"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Tashlab o‘tish"</string>
     <string name="no_matches" msgid="6472699895759164599">"Topilmadi"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Sahifadan topish"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# ta moslik}other{# / {total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Tayyor"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Umumiy xotira tozalanmoqda…"</string>
     <string name="share" msgid="4157615043345227321">"Yuborish"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> orqa fonda ishlamoqda va batareyani ortiqcha sarflamoqda. Tekshirish uchun bosing."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> uzoq vaqt orqa fonda ishlamoqda. Tekshirish uchun bosing."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Faol ilovalarni tekshiring"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Kamera bu qurilma orqali ochilmadi"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 329f193..07a98c9 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"truy cập lịch của bạn"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Tin nhắn SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"gửi và xem tin nhắn SMS"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"Tệp và tài liệu"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"truy cập vào các tệp và tài liệu trên thiết bị của bạn"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"Nhạc và âm thanh khác"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"truy cập vào tệp âm thanh trên thiết bị"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"Ảnh và video"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Hãy nhập phương thức khóa màn hình của bạn để tiếp tục"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"Phát hiện thấy một phần vân tay"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Hãy vệ sinh cảm biến vân tay rồi thử lại"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Vệ sinh cảm biến rồi thử lại"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Nhấn chắc trên cảm biến"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Di chuyển ngón tay quá chậm. Vui lòng thử lại."</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Hãy thử một vân tay khác"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Quá sáng"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mỗi lần, hãy thay đổi vị trí ngón tay một chút"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Không nhận dạng được vân tay"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"Nhấn chắc trên cảm biến"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Đã xác thực khuôn mặt"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Bỏ qua"</string>
     <string name="no_matches" msgid="6472699895759164599">"Không có kết quả nào phù hợp"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Tìm kiếm trên trang"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# kết quả trùng khớp}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Xong"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Đang xóa bộ nhớ dùng chung…"</string>
     <string name="share" msgid="4157615043345227321">"Chia sẻ"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> đang chạy trong nền và làm tiêu hao pin. Nhấn để xem lại."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> đang chạy trong nền trong thời gian dài. Nhấn để xem lại."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Xem các ứng dụng đang hoạt động"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Không sử dụng được máy ảnh trên thiết bị này"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index dbfef83..31a37d2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"访问您的日历"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"短信"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"发送和查看短信"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"文件和文档"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"访问您设备上的文件和文档"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"音乐和其他音频"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"访问您设备上的音频文件"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"照片和视频"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"输入您的屏幕锁定凭据才能继续"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"检测到局部指纹"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"无法处理指纹,请重试。"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"请清洁指纹传感器,然后重试"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"请清洁传感器,然后重试"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"请用力按住传感器"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移动太慢,请重试。"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"请试试其他指纹"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"光线太亮"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"请在每次放手指时略微更改手指的位置"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"未能识别指纹"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"请用力按住传感器"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已验证"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"跳过"</string>
     <string name="no_matches" msgid="6472699895759164599">"无匹配项"</string>
     <string name="find_on_page" msgid="5400537367077438198">"在网页上查找"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# 条匹配结果}other{#/{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"完成"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"正在清空共享的存储空间…"</string>
     <string name="share" msgid="4157615043345227321">"分享"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> 正在后台运行,并且消耗了大量电池电量。点按即可查看。"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> 已在后台运行较长时间。点按即可查看。"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"查看使用中的应用"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"无法使用此设备的摄像头"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index c4fa7a0..07a2e6d 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"存取您的日曆"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"短訊"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"傳送和查看短訊"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"檔案和文件"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"存取裝置上的檔案和文件"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"音樂和其他音訊"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"存取裝置上的音訊檔案"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"相片和影片"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"如要繼續操作,請輸入螢幕鎖定解鎖憑證"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"只偵測到部分指紋"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋。請再試一次。"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"請清潔指紋感應器,然後再試一次"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"請清潔感應器,然後再試一次"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請用力按住感應器"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移動太慢,請重試。"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃瞄時請稍微變更手指的位置"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"無法辨識指紋"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"請用力按住感應器"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已經驗證"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
     <string name="no_matches" msgid="6472699895759164599">"沒有相符的結果"</string>
     <string name="find_on_page" msgid="5400537367077438198">"在頁面中尋找"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# 個相符的項目}other{第 # 個 (共 {total} 個相符的項目)}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"完成"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"正在清除共用儲存空間資料…"</string>
     <string name="share" msgid="4157615043345227321">"分享"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"<xliff:g id="APP">%1$s</xliff:g> 正在背景執行並大量耗電。輕按即可查看。"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"<xliff:g id="APP">%1$s</xliff:g> 已長時間在背景執行。輕按即可查看。"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"查看使用中的應用程式"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"無法存取此裝置的相機"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 843c7517..1f0425f 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -304,10 +304,8 @@
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"存取你的日曆"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"簡訊"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"傳送及查看簡訊"</string>
-    <!-- no translation found for permgrouplab_storage (9173334109512154196) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_storage (8352226729501080525) -->
-    <skip />
+    <string name="permgrouplab_storage" msgid="9173334109512154196">"檔案與文件"</string>
+    <string name="permgroupdesc_storage" msgid="8352226729501080525">"存取裝置上的檔案與文件"</string>
     <string name="permgrouplab_readMediaAural" msgid="5885210465560755316">"音樂和其他音訊"</string>
     <string name="permgroupdesc_readMediaAural" msgid="1170143315714662822">"存取裝置上的音訊檔案"</string>
     <string name="permgrouplab_readMediaVisual" msgid="9137695801926624061">"相片和影片"</string>
@@ -548,7 +546,7 @@
     <string name="permlab_uwb_ranging" msgid="8141915781475770665">"判斷附近超寬頻裝置間的相對位置"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置間的相對位置"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與鄰近的 Wi-Fi 裝置互動"</string>
-    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式向鄰近的 Wi-Fi 裝置廣播、與這些裝置連線,並能判斷這些裝置的相對位置"</string>
+    <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式顯示鄰近的 Wi-Fi 裝置的資料、與其連線並判斷相對位置"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付費服務資訊"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得首選 NFC 付費服務資訊,例如已註冊的輔助工具和路線目的地。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
@@ -588,12 +586,9 @@
     <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"如要繼續操作,請輸入螢幕鎖定憑證"</string>
     <string name="fingerprint_acquired_partial" msgid="694598777291084823">"僅偵測到局部指紋"</string>
     <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋,請再試一次。"</string>
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1770676120848224250) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty_alt (9169582140486372897) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (1628459767349116104) -->
-    <skip />
+    <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"請清潔指紋感應器,然後再試一次"</string>
+    <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"清潔感應器,然後再試一次"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請確實按住感應器"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移動速度過慢,請再試一次。"</string>
     <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
@@ -601,10 +596,8 @@
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃描時請稍微變更手指的位置"</string>
   <string-array name="fingerprint_acquired_vendor">
   </string-array>
-    <!-- no translation found for fingerprint_error_not_match (4599441812893438961) -->
-    <skip />
-    <!-- no translation found for fingerprint_udfps_error_not_match (4709197752023550709) -->
-    <skip />
+    <string name="fingerprint_error_not_match" msgid="4599441812893438961">"指紋辨識失敗"</string>
+    <string name="fingerprint_udfps_error_not_match" msgid="4709197752023550709">"請確實按住感應器"</string>
     <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string>
     <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"臉孔驗證成功"</string>
     <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string>
@@ -1508,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
     <string name="no_matches" msgid="6472699895759164599">"沒有相符項目"</string>
     <string name="find_on_page" msgid="5400537367077438198">"在頁面中尋找"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# 個相符的項目}other{第 # 個,共 {total} 個相符的項目}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"完成"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"正在清除共用儲存空間…"</string>
     <string name="share" msgid="4157615043345227321">"分享"</string>
@@ -2263,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"「<xliff:g id="APP">%1$s</xliff:g>」正在背景運作且耗用大量電力。輕觸即可查看。"</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"「<xliff:g id="APP">%1$s</xliff:g>」已長時間在背景運作。輕觸即可查看。"</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"查看使用中的應用程式"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"無法存取這部裝置的相機"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 41cd8b0..35f6245 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1501,8 +1501,7 @@
     <string name="skip_button_label" msgid="3566599811326688389">"Yeqa"</string>
     <string name="no_matches" msgid="6472699895759164599">"Akukho okufanayo"</string>
     <string name="find_on_page" msgid="5400537367077438198">"Thola ekhasini"</string>
-    <!-- no translation found for matches_found (2296462299979507689) -->
-    <skip />
+    <string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{okufanayo okungu-#}one{okungu-# kokungu-{total}}other{okungu-# kokungu-{total}}}"</string>
     <string name="action_mode_done" msgid="2536182504764803222">"Kwenziwe"</string>
     <string name="progress_erasing" msgid="6891435992721028004">"Isusa isitoreji esabiwe…"</string>
     <string name="share" msgid="4157615043345227321">"Yabelana"</string>
@@ -2256,6 +2255,5 @@
     <string name="notification_content_abusive_bg_apps" msgid="5572096708044958249">"I-<xliff:g id="APP">%1$s</xliff:g> isebenza ngemuva futhi idla ibhethri. Thepha ukuze ubuyekeze."</string>
     <string name="notification_content_long_running_fgs" msgid="8878031652441570178">"I-<xliff:g id="APP">%1$s</xliff:g> isebenza ngemuva isikhathi eside. Thepha ukuze ubuyekeze."</string>
     <string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Hlola ama-app asebenzayo"</string>
-    <!-- no translation found for vdm_camera_access_denied (6345652513729130490) -->
-    <skip />
+    <string name="vdm_camera_access_denied" msgid="6345652513729130490">"Ayikwazi ukufinyelela ikhamera kule divayisi"</string>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index fca2bd1..70ff29b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3364,7 +3364,7 @@
             area for the user and that ideally it should not be covered. Setting this is only
             appropriate for UI where the user would likely take action to uncover it.
             <p>The system will try to respect this, but when not possible will ignore it.
-            See {@link android.view.View#setPreferKeepClear}. -->
+            <p>This is equivalent to {@link android.view.View#setPreferKeepClear}.-->
         <attr name="preferKeepClear" format="boolean" />
 
         <!-- <p>Whether or not the auto handwriting initiation is enabled in this View.
@@ -3644,6 +3644,14 @@
         <attr name="__removed2" format="boolean" />
         <!-- Specifies whether the IME supports showing inline suggestions. -->
         <attr name="supportsInlineSuggestions" format="boolean" />
+        <!-- Specifies whether the IME supports showing inline suggestions when touch
+             exploration is enabled. This does nothing if supportsInlineSuggestions is false.
+             The default value is false and most IMEs should not set this
+             to true since the older menu-style Autofill works better with touch exploration.
+             This attribute should be set to true in special situations, such as if this is an
+             accessibility-focused IME which blocks user interaction with the app window while the
+             IME is displayed. -->
+        <attr name="supportsInlineSuggestionsWithTouchExploration" format="boolean" />
         <!-- Specifies whether the IME suppresses system spell checker.
              The default value is false. If an IME sets this attribute to true,
              the system spell checker will be disabled while the IME has an
@@ -5471,9 +5479,9 @@
             <enum name="none" value="0" />
             <!-- Use the least restrictive rule for line-breaking. -->
             <enum name="loose" value="1" />
-            <!-- Indicate breaking text with the most comment set of line-breaking rules. -->
+            <!-- Indicates breaking text with the most comment set of line-breaking rules. -->
             <enum name="normal" value="2" />
-            <!-- ndicates breaking text with the most strictest line-breaking rules. -->
+            <!-- Indicates breaking text with the most strictest line-breaking rules. -->
             <enum name="strict" value="3" />
         </attr>
         <!-- Specify the phrase-based line break can be used when calculating the text wrapping.-->
@@ -7005,30 +7013,42 @@
 
     <!-- Defines the ExtendAnimation used to extend windows during animations -->
     <declare-styleable name="ExtendAnimation">
-        <!-- Defines the amount a window should be extended outward from the left at
-             the start of the animation. -->
-        <attr name="fromExtendLeft" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the top at
-             the start of the animation. -->
-        <attr name="fromExtendTop" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the right at
-             the start of the animation. -->
-        <attr name="fromExtendRight" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the bottom at
-             the start of the animation. -->
-        <attr name="fromExtendBottom" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the left by
-             the end of the animation by transitioning from the fromExtendLeft amount. -->
-        <attr name="toExtendLeft" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the top by
-             the end of the animation by transitioning from the fromExtendTop amount. -->
-        <attr name="toExtendTop" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the right by
-             the end of the animation by transitioning from the fromExtendRight amount. -->
-        <attr name="toExtendRight" format="float|fraction" />
-        <!-- Defines the amount a window should be extended outward from the bottom by
-             the end of the animation by transitioning from the fromExtendBottom amount. -->
-        <attr name="toExtendBottom" format="float|fraction" />
+        <!-- Defines the amount a window should be extended outward from the left at the start of
+             the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+             provided) or as a percentage of the animation target's width. -->
+        <attr name="fromExtendLeft" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the top at the start of
+             the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+             provided) or as a percentage of the animation target's height. -->
+        <attr name="fromExtendTop" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the right at the start of
+             the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+             provided) or as a percentage of the animation target's width. -->
+        <attr name="fromExtendRight" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the bottom at the start of
+             the animation in an absolute dimension (interpreted as pixels if no dimension unit is
+             provided) or as a percentage of the animation target's height. -->
+        <attr name="fromExtendBottom" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the left by the end of the
+             animation by transitioning from the fromExtendLeft amount in an absolute dimension
+             (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+             animation target's width. -->
+        <attr name="toExtendLeft" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the top by the end of the
+             animation by transitioning from the fromExtendTop amount in an absolute dimension
+             (interpreted as pixels if no dimension unit is provided) or as a percentage of the
+             animation target's height. -->
+        <attr name="toExtendTop" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the right by the end of
+             the animation by transitioning from the fromExtendRight amount in an absolute
+             dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+             of the animation target's width. -->
+        <attr name="toExtendRight" format="float|fraction|dimension" />
+        <!-- Defines the amount a window should be extended outward from the bottom by the end of
+             the animation by transitioning from the fromExtendBottom amount in an absolute
+             dimension (interpreted as pixels if no dimension unit is provided) or as a percentage
+             of the animation target's height. -->
+        <attr name="toExtendBottom" format="float|fraction|dimension" />
     </declare-styleable>
 
     <declare-styleable name="LayoutAnimation">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6dc975b..0e0c6a3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2030,6 +2030,14 @@
         -->
         <attr name="resetEnabledSettingsOnAppDataCleared" format="boolean" />
         <attr name="knownActivityEmbeddingCerts" />
+
+        <!-- If false, {@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK} and
+             {@link android.app.Activity#onBackPressed Activity.onBackPressed()}
+             and related event will be forwarded to the Activities and View, otherwise those events
+             will be replaced by a call to
+             {@link android.view.OnBackInvokedCallback#onBackInvoked
+             OnBackInvokedCallback.onBackInvoked()} on the focused window. -->
+        <attr name="enableOnBackInvokedCallback" format="boolean"/>
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index 82caa26..d7d222c 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -133,8 +133,8 @@
     <!-- The color of the seekbar track background in SeekbarListItem. This color is assumed to be
          on a light-colored background. -->
     <color name="car_seekbar_track_background">@color/car_seekbar_track_background_dark</color>
-    <!-- background is car_grey_868 with .9 alpha -->
-    <color name="car_toast_background">#E6282a2d</color>
+    <!-- background is car_grey_868 with -->
+    <color name="car_toast_background">@color/car_grey_868</color>
 
     <!-- Misc colors -->
     <color name="car_highlight_light">#ff66b5ff</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fdb85a9..05894d5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2902,6 +2902,11 @@
     <string name="config_sensorUseStartedActivity" translatable="false"
             >com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
 
+    <!-- Component name of the activity used to inform a user about a sensory being blocked because
+     of hardware privacy switches. -->
+    <string name="config_sensorUseStartedActivity_hwToggle" translatable="false"
+            >com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
+
     <!-- Component name of the activity used to ask a user to confirm system language change after
          receiving <Set Menu Language> CEC message. -->
     <string name="config_hdmiCecSetMenuLanguageActivity"
@@ -3733,6 +3738,10 @@
          must be no less than 3 for back compatibility. -->
     <integer name="config_pictureInPictureMaxNumberOfActions">3</integer>
 
+    <!-- The behavior when an activity has not specified a preference to dock big overlays or not.
+         Docking puts the activity side-by-side next to the big overlay windows. -->
+    <bool name="config_dockBigOverlayWindows">false</bool>
+
     <!-- Controls the snap mode for the docked stack divider
              0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
              1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
@@ -5473,10 +5482,16 @@
     <!-- On-device package for providing companion device associations. -->
     <string name="config_systemCompanionDeviceProvider" translatable="false"></string>
 
-    <!-- Whether this device is supporting the microphone toggle -->
+    <!-- Whether this device is supporting the software microphone toggle -->
     <bool name="config_supportsMicToggle">false</bool>
     <!-- Whether this device is supporting the camera toggle -->
     <bool name="config_supportsCamToggle">false</bool>
+    <!-- Whether this device is supporting the hardware microphone toggle -->
+    <bool name="config_supportsHardwareMicToggle">false</bool>
+    <!-- Whether this device is supporting the hardware camera toggle -->
+    <bool name="config_supportsHardwareCamToggle">false</bool>
+    <!-- Whether a camera intent is launched when the lens cover is toggled -->
+    <bool name="config_launchCameraOnCameraLensCoverToggle">true</bool>
 
     <!-- List containing the allowed install sources for accessibility service. -->
     <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4874e65..adf8f8e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -606,6 +606,9 @@
     <!-- The padding ratio of the Accessibility icon foreground drawable -->
     <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
 
+    <!-- The minimum window size of the accessibility window magnifier -->
+    <dimen name="accessibility_window_magnifier_min_size">122dp</dimen>
+
     <!-- Margin around the various security views -->
     <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
 
@@ -991,4 +994,11 @@
     <dimen name="secondary_rounded_corner_radius_adjustment">0px</dimen>
     <dimen name="secondary_rounded_corner_radius_top_adjustment">0px</dimen>
     <dimen name="secondary_rounded_corner_radius_bottom_adjustment">0px</dimen>
+
+    <!-- Default size for user icons (a.k.a. avatar images) -->
+    <dimen name="user_icon_size">190dp</dimen>
+
+    <!-- Dimensions for the translations of the default dialog animation. -->
+    <dimen name="popup_enter_animation_from_y_delta">20dp</dimen>
+    <dimen name="popup_exit_animation_to_y_delta">-10dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7bf34a1..3beb4b2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3279,6 +3279,8 @@
     <public name="allowUntrustedActivityEmbedding" />
     <public name="knownActivityEmbeddingCerts" />
     <public name="intro" />
+    <public name="enableOnBackInvokedCallback" />
+    <public name="supportsInlineSuggestionsWithTouchExploration" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index db66777..558e3c3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -375,6 +375,7 @@
   <java-symbol type="string" name="config_usbConfirmActivity" />
   <java-symbol type="string" name="config_usbResolverActivity" />
   <java-symbol type="string" name="config_sensorUseStartedActivity" />
+  <java-symbol type="string" name="config_sensorUseStartedActivity_hwToggle" />
   <java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
@@ -417,6 +418,7 @@
   <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" />
   <java-symbol type="dimen" name="config_pictureInPictureExpandedHorizontalHeight" />
   <java-symbol type="dimen" name="config_pictureInPictureExpandedVerticalWidth" />
+  <java-symbol type="bool" name="config_dockBigOverlayWindows" />
   <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
   <java-symbol type="integer" name="config_burnInProtectionMinHorizontalOffset" />
   <java-symbol type="integer" name="config_burnInProtectionMaxHorizontalOffset" />
@@ -543,6 +545,7 @@
   <java-symbol type="dimen" name="immersive_mode_cling_width" />
   <java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
   <java-symbol type="dimen" name="circular_display_mask_thickness" />
+  <java-symbol type="dimen" name="user_icon_size" />
 
   <java-symbol type="string" name="add_account_button_label" />
   <java-symbol type="string" name="addToDictionary" />
@@ -1673,6 +1676,7 @@
   <java-symbol type="id" name="media_route_volume_slider" />
   <java-symbol type="id" name="media_route_control_frame" />
   <java-symbol type="id" name="media_route_extended_settings_button" />
+  <java-symbol type="id" name="media_route_progress_bar" />
   <java-symbol type="string" name="media_route_chooser_title" />
   <java-symbol type="string" name="media_route_chooser_title_for_remote_display" />
   <java-symbol type="string" name="media_route_controller_disconnect" />
@@ -2233,7 +2237,6 @@
   <java-symbol type="string" name="config_dreamsDefaultComponent" />
   <java-symbol type="array" name="config_supportedDreamComplications" />
   <java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
-  <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" />
@@ -2356,6 +2359,10 @@
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_action" />
   <java-symbol type="string" name="nas_upgrade_notification_learn_more_content" />
   <java-symbol type="bool" name="config_settingsHelpLinksEnabled" />
+  <java-symbol type="integer" name="config_activityDefaultDur" />
+  <java-symbol type="integer" name="config_activityShortDur" />
+  <java-symbol type="dimen" name="popup_enter_animation_from_y_delta" />
+  <java-symbol type="dimen" name="popup_exit_animation_to_y_delta" />
 
   <!-- ImfTest -->
   <java-symbol type="layout" name="auto_complete_list" />
@@ -4392,6 +4399,7 @@
   <java-symbol type="color" name="accessibility_focus_highlight_color" />
   <!-- Width of the outline stroke used by the accessibility focus rectangle -->
   <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" />
+  <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" />
 
   <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" />
 
@@ -4649,6 +4657,9 @@
 
   <java-symbol type="bool" name="config_supportsMicToggle" />
   <java-symbol type="bool" name="config_supportsCamToggle" />
+  <java-symbol type="bool" name="config_supportsHardwareMicToggle" />
+  <java-symbol type="bool" name="config_supportsHardwareCamToggle" />
+  <java-symbol type="bool" name="config_launchCameraOnCameraLensCoverToggle" />
 
   <java-symbol type="dimen" name="starting_surface_icon_size" />
   <java-symbol type="dimen" name="starting_surface_default_icon_size" />
diff --git a/packages/SystemUI/res/layout/idle_host_view.xml b/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
similarity index 64%
rename from packages/SystemUI/res/layout/idle_host_view.xml
rename to core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
index f407874..3440208 100644
--- a/packages/SystemUI/res/layout/idle_host_view.xml
+++ b/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <!--
-  ~ 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.
@@ -15,9 +16,13 @@
   ~ limitations under the License.
   -->
 
-
-<com.android.systemui.idle.IdleHostView
+<input-method
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/idle_host_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:supportsInlineSuggestionsWithTouchExploration="true"
+>
+    <subtype
+        android:label="subtype1"
+        android:imeSubtypeLocale="en_US"
+        android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 8718b95..e7d7d640 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -55,11 +55,13 @@
 
         assertThat(imi.supportsSwitchingToNextInputMethod(), is(false));
         assertThat(imi.isInlineSuggestionsEnabled(), is(false));
+        assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
 
         final InputMethodInfo clone = cloneViaParcel(imi);
 
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
         assertThat(imi.isInlineSuggestionsEnabled(), is(false));
+        assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false));
     }
 
     @Test
@@ -85,6 +87,18 @@
     }
 
     @Test
+    public void testInlineSuggestionsEnabledWithTouchExploration() throws Exception {
+        final InputMethodInfo imi =
+                buildInputMethodForTest(R.xml.ime_meta_inline_suggestions_with_touch_exploration);
+
+        assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.supportsInlineSuggestionsWithTouchExploration(), is(true));
+    }
+
+    @Test
     public void testIsVrOnly() throws Exception {
         final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only);
 
diff --git a/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
new file mode 100644
index 0000000..c6f5924
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.widget;
+
+import static com.android.internal.widget.floatingtoolbar.FloatingToolbar.FLOATING_TOOLBAR_TAG;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.res.Resources;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+final class FloatingToolbarUtils {
+
+    private final UiDevice mDevice;
+
+    FloatingToolbarUtils() {
+        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    }
+
+    void waitForFloatingToolbarPopup() {
+        mDevice.wait(Until.findObject(By.desc(FLOATING_TOOLBAR_TAG)), 500);
+    }
+
+    void assertFloatingToolbarIsDisplayed() {
+        waitForFloatingToolbarPopup();
+        assertThat(mDevice.hasObject(By.desc(FLOATING_TOOLBAR_TAG))).isTrue();
+    }
+
+    void assertFloatingToolbarContainsItem(String itemLabel) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to find item labelled [" + itemLabel + "]")
+                .that(mDevice.hasObject(
+                        By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+                .isTrue();
+    }
+
+    void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to not find item labelled [" + itemLabel + "]")
+                .that(mDevice.hasObject(
+                        By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+                .isFalse();
+    }
+
+    void assertFloatingToolbarContainsItemAtIndex(String itemLabel, int index) {
+        waitForFloatingToolbarPopup();
+        assertWithMessage("Expected to find item labelled [" + itemLabel + "] at index " + index)
+                .that(mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                        .findObjects(By.clickable(true))
+                        .get(index)
+                        .getChildren()
+                        .get(1)
+                        .getText())
+                .isEqualTo(itemLabel);
+    }
+
+    void clickFloatingToolbarItem(String label) {
+        waitForFloatingToolbarPopup();
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.text(label))
+                .click();
+    }
+
+    void clickFloatingToolbarOverflowItem(String label) {
+        // TODO: There might be a benefit to combining this with "clickFloatingToolbarItem" method.
+        waitForFloatingToolbarPopup();
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.desc(str(R.string.floating_toolbar_open_overflow_description)))
+                .click();
+        mDevice.wait(
+                Until.findObject(By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(label))),
+                1000);
+        mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+                .findObject(By.text(label))
+                .click();
+    }
+
+    private static String str(int id) {
+        return Resources.getSystem().getString(id);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 28f9ccc..90844ea 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -17,9 +17,6 @@
 package android.widget;
 
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed;
 import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed;
@@ -72,6 +69,7 @@
     @Rule
     public final ActivityTestRule<TextViewActivity> mActivityRule =
             new ActivityTestRule<>(TextViewActivity.class);
+    private final FloatingToolbarUtils mToolbar = new FloatingToolbarUtils();
 
     private TextViewActivity getActivity() {
         return mActivityRule.getActivity();
@@ -118,22 +116,19 @@
         setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(
-                getActivity().getString(com.android.internal.R.string.replace));
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarOverflowItem(
                 getActivity().getString(com.android.internal.R.string.replace));
 
         assertSuggestionsPopupIsDisplayed();
     }
 
     @Test
-    public void testInsertionActionMode() {
+    public void testInsertionActionMode() throws Throwable {
         final String text = "abc def ghi";
 
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(replaceText(text));
+        Thread.sleep(500);
 
         final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
                 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
@@ -141,10 +136,7 @@
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(
-                getActivity().getString(com.android.internal.R.string.replace));
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarItem(
                 getActivity().getString(com.android.internal.R.string.replace));
 
         assertSuggestionsPopupIsDisplayed();
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 152992c..659cd98 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,15 +16,10 @@
 
 package android.widget;
 
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
 import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.widget.espresso.TextViewActions.Handle;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -64,10 +59,17 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
@@ -79,6 +81,7 @@
 import android.view.MenuItem;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
@@ -102,6 +105,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Tests the TextView widget from an Activity
@@ -116,11 +120,16 @@
 
     private Activity mActivity;
     private Instrumentation mInstrumentation;
+    private UiDevice mDevice;
+    private FloatingToolbarUtils mToolbar;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         mActivity = mActivityRule.getActivity();
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mDevice = UiDevice.getInstance(mInstrumentation);
+        mDevice.wakeUp();
+        mToolbar = new FloatingToolbarUtils();
         TextClassificationManager tcm = mActivity.getSystemService(
                 TextClassificationManager.class);
         tcm.setTextClassifier(TextClassifier.NO_OP);
@@ -132,14 +141,14 @@
         final String helloWorld = "Hello world!";
         // We use replaceText instead of typeTextIntoFocusedView to input text to avoid
         // unintentional interactions with software keyboard.
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
 
         onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
     }
     @Test
     public void testPositionCursorAtTextAtIndex() {
         final String helloWorld = "Hello world!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
 
         // Delete text at specified index and see if we got the right one.
@@ -152,7 +161,7 @@
         // Arabic text. The expected cursorable boundary is
         // | \u0623 \u064F | \u067A | \u0633 \u0652 |
         final String text = "\u0623\u064F\u067A\u0633\u0652";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -172,7 +181,7 @@
     public void testPositionCursorAtTextAtIndex_devanagari() {
         // Devanagari text. The expected cursorable boundary is | \u0915 \u093E |
         final String text = "\u0915\u093E";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
@@ -186,7 +195,7 @@
     public void testLongPressToSelect() {
         final String helloWorld = "Hello Kirk!";
         onView(withId(R.id.textview)).perform(click());
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
 
@@ -196,7 +205,7 @@
     @Test
     public void testLongPressEmptySpace() {
         final String helloWorld = "Hello big round sun!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         // Move cursor somewhere else
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
         // Long-press at end of line.
@@ -210,7 +219,7 @@
     @Test
     public void testLongPressAndDragToSelect() {
         final String helloWorld = "Hello little handsome boy!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
 
@@ -220,7 +229,7 @@
     @Test
     public void testLongPressAndDragToSelect_emoji() {
         final String text = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressAndDragOnText(4, 6));
         onView(withId(R.id.textview)).check(hasSelection("\uD83D\uDE02"));
@@ -234,7 +243,7 @@
     @Test
     public void testDragAndDrop() {
         final String text = "abc def ghi.";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
 
         onView(withId(R.id.textview)).perform(
@@ -254,7 +263,7 @@
     @Test
     public void testDoubleTapToSelect() {
         final String helloWorld = "Hello SuetYi!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
 
         onView(withId(R.id.textview)).perform(
                 doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
@@ -265,7 +274,7 @@
     @Test
     public void testDoubleTapAndDragToSelect() {
         final String helloWorld = "Hello young beautiful person!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(doubleTapAndDragOnText(helloWorld.indexOf("young"),
                         helloWorld.indexOf(" person!")));
 
@@ -275,7 +284,7 @@
     @Test
     public void testDoubleTapAndDragToSelect_multiLine() {
         final String helloWorld = "abcd\n" + "efg\n" + "hijklm\n" + "nop";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 doubleTapAndDragOnText(helloWorld.indexOf("m"), helloWorld.indexOf("a")));
         onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijklm"));
@@ -284,7 +293,7 @@
     @Test
     public void testSelectBackwordsByTouch() {
         final String helloWorld = "Hello king of the Jungle!";
-        onView(withId(R.id.textview)).perform(replaceText(helloWorld));
+        setText(helloWorld);
         onView(withId(R.id.textview)).perform(
                 doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
 
@@ -294,12 +303,11 @@
     @Test
     public void testToolbarAppearsAfterSelection() {
         final String text = "Toolbar appears after selection.";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(
                 longPressOnTextAtIndex(text.indexOf("appears")));
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -317,13 +325,12 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        onView(withId(R.id.textview)).perform(replaceText("test"));
+        setText("test");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.cut));
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
 
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -331,8 +338,7 @@
         TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.textview);
         int position = (textLink.getStart() + textLink.getEnd()) / 2;
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -342,23 +348,20 @@
         final int position = (textLink.getStart() + textLink.getEnd()) / 2;
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         // toggle
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
+        mToolbar.waitForFloatingToolbarPopup();
         assertFalse(textView.hasSelection());
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         // click outside
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
         assertFalse(textView.hasSelection());
     }
 
@@ -372,8 +375,7 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
     }
 
     @Test
@@ -385,7 +387,7 @@
             final TextView textView = mActivity.findViewById(R.id.textview);
             textView.setText(text);
             textView.setCustomSelectionActionModeCallback(
-                    new ActionMode.Callback() {
+                    new ActionModeCallbackAdapter() {
                         @Override
                         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                             menu.clear();
@@ -398,29 +400,19 @@
                             clickedItem[0] = item;
                             return true;
                         }
-
-                        @Override
-                        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                            return true;
-                        }
-
-                        @Override
-                        public void onDestroyActionMode(ActionMode mode) {}
                     });
         });
         mInstrumentation.waitForIdleSync();
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
-        sleepForFloatingToolbarPopup();
 
         // Change the selection so that the menu items are refreshed.
         final TextView textView = mActivity.findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, 0));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        clickFloatingToolbarItem("Item");
+        mToolbar.clickFloatingToolbarItem("Item");
         mInstrumentation.waitForIdleSync();
 
         assertEquals(latestItem[0], clickedItem[0]);
@@ -469,13 +461,11 @@
         mActivityRule.runOnUiThread(() -> textView.setFocusableInTouchMode(true));
 
         onView(withId(R.id.nonselectable_textview)).perform(clickOnTextAtIndex(position));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(textView.hasSelection());
 
         mActivityRule.runOnUiThread(() -> textView.clearFocus());
         mInstrumentation.waitForIdleSync();
-        sleepForFloatingToolbarPopup();
 
         assertFalse(textView.hasSelection());
     }
@@ -488,14 +478,12 @@
 
         onView(withId(R.id.nonselectable_textview))
                 .perform(clickOnTextAtIndex(nonselectablePosition));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         assertTrue(nonselectableTextView.hasSelection());
 
-        UiDevice device = UiDevice.getInstance(mInstrumentation);
-        device.openNotification();
+        mDevice.openNotification();
         Thread.sleep(2000);
-        device.pressBack();
+        mDevice.pressBack();
         Thread.sleep(2000);
 
         assertFalse(nonselectableTextView.hasSelection());
@@ -528,62 +516,58 @@
     }
 
     @Test
-    public void testToolbarAndInsertionHandle() {
+    public void testToolbarAndInsertionHandle() throws Throwable {
         final String text = "text";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
+        Thread.sleep(500);
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
 
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.cut));
     }
 
     @Test
     public void testToolbarAndSelectionHandle() {
         final String text = "abcd efg hijk";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.cut));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
         onHandleView(com.android.internal.R.id.selection_end_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
 
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.cut));
     }
 
     @Test
     public void testInsertionHandle() {
         final String text = "abcd efg hijk ";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -602,7 +586,7 @@
     @Test
     public void testInsertionHandle_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -640,7 +624,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -654,7 +638,7 @@
         enableFlagsForInsertionHandleGestures();
         final TextView textView = mActivity.findViewById(R.id.textview);
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -670,7 +654,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -685,7 +669,7 @@
         final TextView textView = mActivity.findViewById(R.id.textview);
 
         final String text = "hello the world";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
@@ -698,7 +682,7 @@
     @Test
     public void testSelectionHandles() {
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
@@ -720,7 +704,7 @@
     @Test
     public void testSelectionHandles_bidi() {
         final String text = "abc \u0621\u0622\u0623 def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0622')));
 
@@ -762,7 +746,7 @@
     @Test
     public void testSelectionHandles_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -790,7 +774,7 @@
         final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
                 + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
                 + "\u0639\u063A\u063B";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('\u0634')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -817,7 +801,7 @@
     @Test
     public void testSelectionHandles_doesNotPassAnotherHandle() {
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -834,7 +818,7 @@
     @Test
     public void testSelectionHandles_doesNotPassAnotherHandle_multiLine() {
         final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -851,7 +835,7 @@
     @Test
     public void testSelectionHandles_snapToWordBoundary() {
         final String text = "abcd efg hijk lmn opqr";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('i')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -904,7 +888,7 @@
     @Test
     public void testSelectionHandles_snapToWordBoundary_multiLine() {
         final String text = "abcd efg\n" + "hijk lmn\n" + "opqr stu";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('m')));
 
         final TextView textView = mActivity.findViewById(R.id.textview);
@@ -939,7 +923,7 @@
     @Test
     public void testSelectionHandles_visibleEvenWithEmptyMenu() {
         ((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                         menu.clear();
@@ -951,17 +935,9 @@
                         menu.clear();
                         return true;
                     }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode mode) {}
                 });
         final String text = "abcd efg hijk lmn";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
 
@@ -982,7 +958,7 @@
         textView.setCustomSelectionActionModeCallback(amCallback);
 
         final String text = "abc def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
         mInstrumentation.waitForIdleSync();
@@ -991,15 +967,13 @@
         // Make sure that "Select All" is included in the selection action mode when the entire text
         // is not selected.
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         // Changing the selection range by API should not interrupt the selection action mode.
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
         mInstrumentation.waitForIdleSync();
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
         // Make sure that "Select All" is no longer included when the entire text is selected by
         // API.
@@ -1007,9 +981,8 @@
                 () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.selectAll));
         // Make sure that shrinking the selection range to cursor (an empty range) by API
         // terminates selection action mode and does not trigger the insertion action mode.
@@ -1020,17 +993,15 @@
         // Make sure that user click can trigger the insertion action mode.
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
         onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarIsDisplayed();
         // Make sure that an existing insertion action mode keeps alive after the insertion point is
         // moved by API.
         mActivityRule.runOnUiThread(
                 () -> Selection.setSelection((Spannable) textView.getText(), 0));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.copy));
         // Make sure that selection action mode is started after selection is created by API when
         // insertion action mode is active.
@@ -1038,16 +1009,15 @@
                 () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
         mInstrumentation.waitForIdleSync();
 
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarIsDisplayed();
-        assertFloatingToolbarContainsItem(
+        mToolbar.assertFloatingToolbarIsDisplayed();
+        mToolbar.assertFloatingToolbarContainsItem(
                 mActivity.getString(com.android.internal.R.string.copy));
     }
 
     @Test
     public void testTransientState() throws Throwable {
         final String text = "abc def";
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
 
         final TextView textView = mActivity.findViewById(R.id.textview);
         assertFalse(textView.hasTransientState());
@@ -1068,58 +1038,43 @@
 
     @Test
     public void testResetMenuItemTitle() throws Throwable {
-        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
+        mActivity.getSystemService(TextClassificationManager.class)
+                .setTextClassifier(TextClassifier.NO_OP);
         final TextView textView = mActivity.findViewById(R.id.textview);
         final int itemId = 1;
-        final String title1 = " AFIGBO";
-        final int index = title1.indexOf('I');
-        final String title2 = title1.substring(index);
+        final String title1 = "@AFIGBO";
+        final int index = 3;
+        final String title2 = "IGBO";
         final String[] title = new String[]{title1};
         mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
-                    @Override
-                    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
-                        return true;
-                    }
-
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
-                        menu.removeItem(itemId);
+                        menu.clear();
                         menu.add(Menu.NONE /* group */, itemId, 0 /* order */, title[0]);
                         return true;
                     }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode actionMode) {
-                    }
                 }));
         mInstrumentation.waitForIdleSync();
 
-        onView(withId(R.id.textview)).perform(replaceText(title1));
+        setText(title1);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(index));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(title1);
+        mToolbar.assertFloatingToolbarContainsItem(title1);
 
         // Change the menu item title.
         title[0] = title2;
         // Change the selection to invalidate the action mode without restarting it.
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, index));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarContainsItem(title2);
+        mToolbar.assertFloatingToolbarContainsItem(title2);
     }
 
     @Test
     public void testAssistItemIsAtIndexZero() throws Throwable {
-        useSystemDefaultTextClassifier();
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
-                new ActionMode.Callback() {
+                new ActionModeCallbackAdapter() {
                     @Override
                     public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
                         // Create another item at order position 0 to confirm that it will never be
@@ -1127,33 +1082,19 @@
                         menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
                         return true;
                     }
-
-                    @Override
-                    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
-                        return true;
-                    }
-
-                    @Override
-                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
-                        return false;
-                    }
-
-                    @Override
-                    public void onDestroyActionMode(ActionMode actionMode) {
-                    }
                 }));
         mInstrumentation.waitForIdleSync();
         final String text = "droid@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
+        mToolbar.assertFloatingToolbarContainsItemAtIndex(tc.getActionLabel(), 0);
     }
 
     @Test
     public void testNoAssistItemForPasswordField() throws Throwable {
-        useSystemDefaultTextClassifier();
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
+
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> {
             textView.setInputType(
@@ -1162,23 +1103,22 @@
         mInstrumentation.waitForIdleSync();
         final String password = "afigbo@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(password));
+        setText(password);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+        mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
     }
 
     @Test
     public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
-        useSystemDefaultTextClassifier();
+        // NOTE: This test addresses a security bug.
+        final SingleActionTextClassifier tc = useSingleActionTextClassifier();
         final String text = "\u202Emoc.diordna.com";
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setText(text));
         mInstrumentation.waitForIdleSync();
 
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
-        sleepForFloatingToolbarPopup();
-        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+        mToolbar.assertFloatingToolbarDoesNotContainItem(tc.getActionLabel());
     }
 
     @Test
@@ -1195,10 +1135,9 @@
         mInstrumentation.waitForIdleSync();
         final String text = "andyroid@android.com";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
         mInstrumentation.waitForIdleSync();
 
         final SelectionEvent lastEvent = selectionEvents.get(selectionEvents.size() - 1);
@@ -1214,9 +1153,8 @@
 
         final String text = "My number is 987654321";
 
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
-        sleepForFloatingToolbarPopup();
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         mInstrumentation.waitForIdleSync();
 
@@ -1247,9 +1185,8 @@
         final String text = "My number is 987654321";
 
         // Long press to trigger selection
-        onView(withId(R.id.textview)).perform(replaceText(text));
+        setText(text);
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
-        sleepForFloatingToolbarPopup();
 
         // Type over the selection
         onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_A));
@@ -1291,16 +1228,14 @@
         });
 
         // Long press to trigger selection
-        onView(withId(R.id.textview)).perform(replaceText("android.com"));
+        setText("android.com");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
         // Click "Copy" to dismiss the selection.
-        clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
+        mToolbar.clickFloatingToolbarItem(mActivity.getString(com.android.internal.R.string.copy));
 
         // Long press to trigger another selection
-        onView(withId(R.id.textview)).perform(replaceText("android@android.com"));
+        setText("android@android.com");
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(0));
-        sleepForFloatingToolbarPopup();
 
         // suggestSelection should be called in two different TextClassifier sessions.
         assertEquals(2, testableTextClassifiers.size());
@@ -1312,10 +1247,9 @@
     public void testPastePlainText_menuAction() {
         initializeClipboardWithText(TextStyle.STYLED);
 
-        onView(withId(R.id.textview)).perform(replaceText(""));
+        setText("");
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
-        clickFloatingToolbarItem(
+        mToolbar.clickFloatingToolbarItem(
                 mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
         mInstrumentation.waitForIdleSync();
 
@@ -1327,18 +1261,33 @@
     public void testPastePlainText_noMenuItemForPlainText() {
         initializeClipboardWithText(TextStyle.PLAIN);
 
-        onView(withId(R.id.textview)).perform(replaceText(""));
+        setText("");
         onView(withId(R.id.textview)).perform(longClick());
-        sleepForFloatingToolbarPopup();
 
-        assertFloatingToolbarDoesNotContainItem(
+        mToolbar.assertFloatingToolbarDoesNotContainItem(
                 mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
     }
 
+    private void setText(String text) {
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        mDevice.wait(Until.findObject(By.text(text)), 1000);
+        mInstrumentation.waitForIdleSync();
+    }
+
     private void useSystemDefaultTextClassifier() {
         mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
     }
 
+    private SingleActionTextClassifier useSingleActionTextClassifier() {
+        useSystemDefaultTextClassifier();
+        final TextClassificationManager tcm =
+                mActivity.getSystemService(TextClassificationManager.class);
+        final SingleActionTextClassifier oneActionTC =
+                new SingleActionTextClassifier(mActivity, tcm.getTextClassifier());
+        tcm.setTextClassifier(oneActionTC);
+        return oneActionTC;
+    }
+
     private void initializeClipboardWithText(TextStyle textStyle) {
         final ClipData clip;
         switch (textStyle) {
@@ -1360,7 +1309,7 @@
         PLAIN, STYLED
     }
 
-    private final class TestableTextClassifier implements TextClassifier {
+    private static final class TestableTextClassifier implements TextClassifier {
         final List<SelectionEvent> mSelectionEvents = new ArrayList<>();
         final List<TextSelection.Request> mTextSelectionRequests = new ArrayList<>();
 
@@ -1385,4 +1334,54 @@
             return mTextSelectionRequests;
         }
     }
+
+    private static final class SingleActionTextClassifier implements TextClassifier {
+
+        private final RemoteAction mAction;
+        private final TextClassifier mOriginal;
+        private final TextClassification mClassificationResult;
+
+        SingleActionTextClassifier(Context context, TextClassifier original) {
+            mAction = new RemoteAction(
+                    Icon.createWithResource(context, android.R.drawable.btn_star),
+                    "assist",
+                    "assist",
+                    PendingIntent.getActivity(context, 0, new Intent(), FLAG_IMMUTABLE));
+            mClassificationResult = new TextClassification.Builder().addAction(mAction).build();
+            mOriginal = Objects.requireNonNull(original);
+        }
+
+        public String getActionLabel() {
+            return mAction.getTitle().toString();
+        }
+
+        @Override
+        public TextSelection suggestSelection(TextSelection.Request request) {
+            final TextSelection sel = mOriginal.suggestSelection(request);
+            return new TextSelection.Builder(
+                    sel.getSelectionStartIndex(), sel.getSelectionEndIndex())
+                    .setTextClassification(mClassificationResult)
+                    .build();
+        }
+    }
+
+    private static class ActionModeCallbackAdapter implements ActionMode.Callback {
+        @Override
+        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+            return true;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode actionMode) {}
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
deleted file mode 100644
index 4f95cb8..0000000
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.widget.espresso;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
-import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static com.android.internal.widget.floatingtoolbar.LocalFloatingToolbarPopup.MenuItemRepr;
-
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.is;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.test.espresso.NoMatchingRootException;
-import androidx.test.espresso.NoMatchingViewException;
-import androidx.test.espresso.UiController;
-import androidx.test.espresso.ViewAction;
-import androidx.test.espresso.ViewInteraction;
-
-import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-
-/**
- * Espresso utility methods for the floating toolbar.
- */
-public class FloatingToolbarEspressoUtils {
-    private final static Object TAG = FloatingToolbar.FLOATING_TOOLBAR_TAG;
-
-    private FloatingToolbarEspressoUtils() {}
-
-    private static ViewInteraction onFloatingToolBar() {
-        return onView(withTagValue(is(TAG)))
-                .inRoot(allOf(
-                        isPlatformPopup(),
-                        withDecorView(hasDescendant(withTagValue(is(TAG))))));
-    }
-
-    /**
-     * Creates a {@link ViewInteraction} for the floating bar menu item with the given matcher.
-     *
-     * @param matcher The matcher for the menu item.
-     */
-    public static ViewInteraction onFloatingToolBarItem(Matcher<View> matcher) {
-        return onView(matcher)
-                .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
-    }
-
-    /**
-     * Asserts that the floating toolbar is displayed on screen.
-     *
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarIsDisplayed() {
-        onFloatingToolBar().check(matches(isDisplayed()));
-    }
-
-    /**
-     * Asserts that the floating toolbar is not displayed on screen.
-     *
-     * @throws AssertionError if the assertion fails
-     * @deprecated Negative assertions are taking too long to timeout in Espresso.
-     */
-    @Deprecated
-    public static void assertFloatingToolbarIsNotDisplayed() {
-        try {
-            onFloatingToolBar().check(matches(isDisplayed()));
-        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
-            return;
-        }
-        throw new AssertionError("Floating toolbar is displayed");
-    }
-
-    private static void toggleOverflow() {
-        final int id = com.android.internal.R.id.overflow;
-        onView(allOf(withId(id), isDisplayed()))
-                .inRoot(withDecorView(hasDescendant(withId(id))))
-                .perform(click());
-        onView(isRoot()).perform(SLEEP);
-    }
-
-    public static void sleepForFloatingToolbarPopup() {
-        onView(isRoot()).perform(SLEEP);
-    }
-
-    /**
-     * Asserts that the floating toolbar contains the specified item.
-     *
-     * @param itemLabel label of the item.
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarContainsItem(String itemLabel) {
-        try{
-            onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
-        } catch (AssertionError e) {
-            try{
-                toggleOverflow();
-            } catch (NoMatchingViewException | NoMatchingRootException e2) {
-                // No overflow items.
-                throw e;
-            }
-            try{
-                onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
-            } finally {
-                toggleOverflow();
-            }
-        }
-    }
-
-    /**
-     * Asserts that the floating toolbar contains a specified item at a specified index.
-     *
-     * @param menuItemId id of the menu item
-     * @param index expected index of the menu item in the floating toolbar
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) {
-        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
-            private List<Integer> menuItemIds = new ArrayList<>();
-
-            @Override
-            public boolean matchesSafely(View view) {
-                collectMenuItemIds(view);
-                return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId;
-            }
-
-            @Override
-            public void describeTo(Description description) {}
-
-            private void collectMenuItemIds(View view) {
-                if (view.getTag() instanceof MenuItemRepr) {
-                    menuItemIds.add(((MenuItemRepr) view.getTag()).itemId);
-                } else if (view instanceof ViewGroup) {
-                    ViewGroup viewGroup = (ViewGroup) view;
-                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        collectMenuItemIds(viewGroup.getChildAt(i));
-                    }
-                }
-            }
-        }));
-    }
-
-    /**
-     * Asserts that the floating toolbar doesn't contain the specified item.
-     *
-     * @param itemLabel label of the item.
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
-        final Predicate<View> hasMenuItemLabel = view ->
-                view.getTag() instanceof MenuItemRepr
-                        && itemLabel.equals(((MenuItemRepr) view.getTag()).title);
-        assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
-    }
-
-    /**
-     * Asserts that the floating toolbar does not contain a menu item with the specified id.
-     *
-     * @param menuItemId id of the menu item
-     * @throws AssertionError if the assertion fails
-     */
-    public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
-        final Predicate<View> hasMenuItemId = view ->
-                view.getTag() instanceof MenuItemRepr
-                        && ((MenuItemRepr) view.getTag()).itemId == menuItemId;
-        assertFloatingToolbarMenuItem(hasMenuItemId, false);
-    }
-
-    private static void assertFloatingToolbarMenuItem(
-            final Predicate<View> predicate, final boolean positiveAssertion) {
-        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
-            @Override
-            public boolean matchesSafely(View view) {
-                return positiveAssertion == containsItem(view);
-            }
-
-            @Override
-            public void describeTo(Description description) {}
-
-            private boolean containsItem(View view) {
-                if (predicate.test(view)) {
-                    return true;
-                } else if (view instanceof ViewGroup) {
-                    ViewGroup viewGroup = (ViewGroup) view;
-                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        if (containsItem(viewGroup.getChildAt(i))) {
-                            return true;
-                        }
-                    }
-                }
-                return false;
-            }
-        }));
-    }
-
-    /**
-     * Click specified item on the floating tool bar.
-     *
-     * @param itemLabel label of the item.
-     */
-    public static void clickFloatingToolbarItem(String itemLabel) {
-        try{
-            onFloatingToolBarItem(withText(itemLabel)).check(matches(isDisplayed()));
-        } catch (AssertionError e) {
-            // Try to find the item in the overflow menu.
-            toggleOverflow();
-        }
-        onFloatingToolBarItem(withText(itemLabel)).perform(click());
-    }
-
-    /**
-     * ViewAction to sleep to wait floating toolbar's animation.
-     */
-    private static final ViewAction SLEEP = new ViewAction() {
-        private static final long SLEEP_DURATION = 400;
-
-        @Override
-        public Matcher<View> getConstraints() {
-            return isDisplayed();
-        }
-
-        @Override
-        public String getDescription() {
-            return "Sleep " + SLEEP_DURATION + " ms.";
-        }
-
-        @Override
-        public void perform(UiController uiController, View view) {
-            uiController.loopMainThreadForAtLeast(SLEEP_DURATION);
-        }
-    };
-}
diff --git a/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
index bf508db..2dadb20 100644
--- a/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
+++ b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
@@ -53,14 +53,15 @@
     @Test
     public void testRegisterAndUnregister() {
 
-        final TaskFpsCallback.OnFpsCallbackListener listener = fps -> {
-            // Ignore
+        final TaskFpsCallback callback = new TaskFpsCallback() {
+            @Override
+            public void onFpsReported(float fps) {
+                // Ignore
+            }
         };
-        final TaskFpsCallback callback = new TaskFpsCallback(Runnable::run, listener);
-
         final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1);
         assertEquals(tasks.size(), 1);
-        mWindowManager.registerTaskFpsCallback(tasks.get(0).taskId, callback);
+        mWindowManager.registerTaskFpsCallback(tasks.get(0).taskId, Runnable::run, callback);
         mWindowManager.unregisterTaskFpsCallback(callback);
     }
 }
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 52cb9f3..a52d2e8 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -86,11 +86,13 @@
         mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                 null /* options */);
 
-        assertThat(mController.mAttachedToDisplayArea).isTrue();
+        assertThat(mController.mAttachedToDisplayArea).isEqualTo(
+                WindowContextController.AttachStatus.STATUS_ATTACHED);
 
         mController.detachIfNeeded();
 
-        assertThat(mController.mAttachedToDisplayArea).isFalse();
+        assertThat(mController.mAttachedToDisplayArea).isEqualTo(
+                WindowContextController.AttachStatus.STATUS_DETACHED);
     }
 
     @Test(expected = IllegalStateException.class)
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index cd42a34..23ec3ea 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -2020,6 +2020,7 @@
                 .check(matches(isDisplayed()));
     }
 
+    @Ignore // b/220067877
     @Test
     public void testWorkTab_xProfileOff_noAppsAvailable_workOff_xProfileOffEmptyStateShown() {
         // enable the work tab feature flag
@@ -2304,6 +2305,7 @@
         assertThat(logger.numCalls(), is(5));
     }
 
+    @Ignore // b/220067877
     @Test
     public void testCopyTextToClipboardLogging() throws Exception {
         Intent sendIntent = createSendTextIntent();
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index a2bc77a..3a27225 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -29,6 +29,7 @@
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
 
 /**
  * Unit test for {@link AndroidFuture}.
@@ -154,4 +155,35 @@
                 expectThrows(ExecutionException.class, future1::get);
         assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
     }
+
+    @Test
+    public void testThenCombine() throws Exception {
+        String nearFutureString = "near future comes";
+        AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+        String farFutureString = " before far future.";
+        AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+        AndroidFuture<String> combinedFuture =
+                nearFuture.thenCombine(farFuture, ((s1, s2) -> s1 + s2));
+
+        assertThat(combinedFuture.get()).isEqualTo(nearFutureString + farFutureString);
+    }
+
+    @Test
+    public void testThenCombine_functionThrowingException() throws Exception {
+        String nearFutureString = "near future comes";
+        AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+        String farFutureString = " before far future.";
+        AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+        UnsupportedOperationException exception = new UnsupportedOperationException(
+                "Unsupported operation exception thrown!");
+        BiFunction<String, String, String> throwingFunction = (s1, s2) -> {
+            throw exception;
+        };
+        AndroidFuture<String> combinedFuture = nearFuture.thenCombine(farFuture, throwingFunction);
+
+        ExecutionException thrown = expectThrows(ExecutionException.class,
+                () -> combinedFuture.get());
+
+        assertThat(thrown.getCause()).isSameInstanceAs(exception);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index f5cbffb..e79f1e3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -24,6 +24,8 @@
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
 import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.app.ActivityManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.HistoryItem;
@@ -213,6 +215,116 @@
         assertEquals(120_000, bgTime);
     }
 
+    /**
+     * Test BatteryStatsImpl.Uid.noteLongPartialWakelockStart for an isolated uid.
+     */
+    @SmallTest
+    public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception {
+        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+
+        bi.setRecordAllHistoryLocked(true);
+        bi.forceRecordAllHistory();
+
+        int pid = 10;
+        String name = "name";
+        String historyName = "historyName";
+
+        WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+        isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+        // Map ISOLATED_UID to UID.
+        bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+        bi.noteLongPartialWakelockStart(name, historyName, ISOLATED_UID);
+
+        clocks.realtime = clocks.uptime = 100;
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        clocks.realtime = clocks.uptime = 220;
+        bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
+
+        final BatteryStatsHistoryIterator iterator =
+                bi.createBatteryStatsHistoryIterator();
+
+        BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+
+        while (iterator.next(item)) {
+            if (item.eventCode == HistoryItem.EVENT_LONG_WAKE_LOCK_START) break;
+        }
+        assertThat(item.eventCode).isEqualTo(HistoryItem.EVENT_LONG_WAKE_LOCK_START);
+        assertThat(item.eventTag).isNotNull();
+        assertThat(item.eventTag.string).isEqualTo(historyName);
+        assertThat(item.eventTag.uid).isEqualTo(UID);
+
+        while (iterator.next(item)) {
+            if (item.eventCode == HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH) break;
+        }
+        assertThat(item.eventCode).isEqualTo(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH);
+        assertThat(item.eventTag).isNotNull();
+        assertThat(item.eventTag.string).isEqualTo(historyName);
+        assertThat(item.eventTag.uid).isEqualTo(UID);
+    }
+
+    /**
+     * Test BatteryStatsImpl.Uid.noteLongPartialWakelockStart for an isolated uid.
+     */
+    @SmallTest
+    public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception {
+        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+
+        bi.setRecordAllHistoryLocked(true);
+        bi.forceRecordAllHistory();
+
+        int pid = 10;
+        String name = "name";
+        String historyName = "historyName";
+
+        WorkSource.WorkChain isolatedWorkChain = new WorkSource.WorkChain();
+        isolatedWorkChain.addNode(ISOLATED_UID, name);
+
+        // Map ISOLATED_UID to UID.
+        bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
+        bi.noteLongPartialWakelockStart(name, historyName, ISOLATED_UID);
+
+        clocks.realtime = clocks.uptime = 100;
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        clocks.realtime = clocks.uptime = 150;
+        bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+
+        clocks.realtime = clocks.uptime = 220;
+        bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
+
+        final BatteryStatsHistoryIterator iterator =
+                bi.createBatteryStatsHistoryIterator();
+
+        BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+
+        while (iterator.next(item)) {
+            if (item.eventCode == HistoryItem.EVENT_LONG_WAKE_LOCK_START) break;
+        }
+        assertThat(item.eventCode).isEqualTo(HistoryItem.EVENT_LONG_WAKE_LOCK_START);
+        assertThat(item.eventTag).isNotNull();
+        assertThat(item.eventTag.string).isEqualTo(historyName);
+        assertThat(item.eventTag.uid).isEqualTo(UID);
+
+        while (iterator.next(item)) {
+            if (item.eventCode == HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH) break;
+        }
+        assertThat(item.eventCode).isEqualTo(HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH);
+        assertThat(item.eventTag).isNotNull();
+        assertThat(item.eventTag.string).isEqualTo(historyName);
+        assertThat(item.eventTag.uid).isEqualTo(UID);
+    }
 
     /**
      * Test BatteryStatsImpl.noteUidProcessStateLocked.
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 88228f2..2a82d8e 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C} 2018 The Android Open Source Project
+// Copyright (C) 2018 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License"};
 // you may not use this file except in compliance with the License.
@@ -140,13 +140,6 @@
 }
 
 prebuilt_etc {
-    name: "privapp_whitelist_com.android.networkstack.tethering",
-    sub_dir: "permissions",
-    src: "com.android.networkstack.tethering.xml",
-    filename_from_src: true,
-}
-
-prebuilt_etc {
     name: "privapp_whitelist_com.android.provision",
     system_ext_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.networkstack.tethering.xml b/data/etc/com.android.networkstack.tethering.xml
deleted file mode 100644
index f26a961..0000000
--- a/data/etc/com.android.networkstack.tethering.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
--->
-
-<permissions>
-    <privapp-permissions package="com.android.networkstack.tethering">
-        <permission name="android.permission.BLUETOOTH_PRIVILEGED" />
-        <permission name="android.permission.MANAGE_USB"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
-        <permission name="android.permission.TETHER_PRIVILEGED"/>
-        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
-        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
-      </privapp-permissions>
-</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ac5daf0..2d2c03b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -481,6 +481,9 @@
         <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
         <permission name="android.permission.NEARBY_WIFI_DEVICES" />
         <permission name="android.permission.OVERRIDE_WIFI_CONFIG" />
+        <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover
+             P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. -->
+        <permission name="android.permission.MANAGE_WIFI_AUTO_JOIN" />
         <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
         <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
@@ -541,6 +544,8 @@
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
         <permission name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
+        <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+        <permission name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 4aa0f07..1779655 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2653,6 +2653,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "664667685": {
+      "message": "Activity %s: enableOnBackInvokedCallback=false. Returning null BackNavigationInfo.",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "665256544": {
       "message": "All windows drawn!",
       "level": "DEBUG",
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index a927f53..8f32f0e 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -1,4 +1,3 @@
-
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -42,8 +41,10 @@
     static_libs: [
         "truth-prebuilt",
         "kxml2-2.3.0",
+        "compile-testing-prebuilt",
         "error_prone_android_framework_lib",
         "error_prone_test_helpers",
+        "google_java_format",
         "hamcrest-library",
         "hamcrest",
         "platform-test-annotations",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
new file mode 100644
index 0000000..07f1d4a
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.LinkType.NONE;
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Description.NO_MATCH;
+import static com.google.errorprone.util.ASTHelpers.getStartPosition;
+import static com.google.errorprone.util.ASTHelpers.getSymbol;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneToken;
+import com.google.errorprone.util.ErrorProneTokens;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.parser.Tokens;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.lang.model.element.ElementKind;
+
+/**
+ * Bug checker to warn about {@code @hide} directives in comments.
+ *
+ * {@code @hide} tags are only meaningful inside of Javadoc comments. Errorprone has checks for
+ * standard Javadoc tags but doesn't know anything about {@code @hide} since it's an Android
+ * specific tag.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+        name = "AndroidHideInComments",
+        summary = "Warns when there are @hide declarations in comments rather than javadoc",
+        linkType = NONE,
+        severity = WARNING)
+public class HideInCommentsChecker extends BugChecker implements
+        BugChecker.CompilationUnitTreeMatcher {
+
+    @Override
+    public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
+        final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree);
+        final String sourceCode = state.getSourceCode().toString();
+        for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
+            for (Tokens.Comment comment : token.comments()) {
+                if (!javadocableTrees.containsKey(token.pos())) {
+                    continue;
+                }
+                generateFix(comment).ifPresent(fix -> {
+                    final Tree javadocableTree = javadocableTrees.get(token.pos());
+                    state.reportMatch(describeMatch(javadocableTree, fix));
+                });
+            }
+        }
+        // We might have multiple matches, so report them via VisitorState rather than the return
+        // value from the match function.
+        return NO_MATCH;
+    }
+
+    private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) {
+        final String text = comment.getText();
+        if (text.startsWith("/**")) {
+            return Optional.empty();
+        }
+
+        if (!text.contains("@hide")) {
+            return Optional.empty();
+        }
+
+        if (text.startsWith("/*")) {
+            final int pos = comment.getSourcePos(1);
+            return Optional.of(SuggestedFix.replace(pos, pos, "*"));
+        } else if (text.startsWith("//")) {
+            final int endPos = comment.getSourcePos(text.length() - 1);
+            final char endChar = text.charAt(text.length() - 1);
+            String javadocClose = " */";
+            if (endChar != ' ') {
+                javadocClose = endChar + javadocClose;
+            }
+            final SuggestedFix fix = SuggestedFix.builder()
+                    .replace(comment.getSourcePos(1), comment.getSourcePos(2), "**")
+                    .replace(endPos, endPos + 1, javadocClose)
+                    .build();
+            return Optional.of(fix);
+        }
+
+        return Optional.empty();
+    }
+
+
+    private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) {
+        Map<Integer, Tree> javadoccableTrees = new HashMap<>();
+        new SuppressibleTreePathScanner<Void, Void>() {
+            @Override
+            public Void visitClass(ClassTree classTree, Void unused) {
+                javadoccableTrees.put(getStartPosition(classTree), classTree);
+                return super.visitClass(classTree, null);
+            }
+
+            @Override
+            public Void visitMethod(MethodTree methodTree, Void unused) {
+                // Generated constructors never have comments
+                if (!ASTHelpers.isGeneratedConstructor(methodTree)) {
+                    javadoccableTrees.put(getStartPosition(methodTree), methodTree);
+                }
+                return super.visitMethod(methodTree, null);
+            }
+
+            @Override
+            public Void visitVariable(VariableTree variableTree, Void unused) {
+                ElementKind kind = getSymbol(variableTree).getKind();
+                if (kind == ElementKind.FIELD) {
+                    javadoccableTrees.put(getStartPosition(variableTree), variableTree);
+                }
+                if (kind == ElementKind.ENUM_CONSTANT) {
+                    javadoccableTrees.put(getStartPosition(variableTree), variableTree);
+                    if (variableTree.getInitializer() instanceof NewClassTree) {
+                        // Skip the generated class definition
+                        ClassTree classBody =
+                                ((NewClassTree) variableTree.getInitializer()).getClassBody();
+                        if (classBody != null) {
+                            scan(classBody.getMembers(), null);
+                        }
+                        return null;
+                    }
+                }
+                return super.visitVariable(variableTree, null);
+            }
+
+        }.scan(tree, null);
+        return javadoccableTrees;
+    }
+
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java
new file mode 100644
index 0000000..f3e6c727
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/HideInCommentsCheckerTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class HideInCommentsCheckerTest {
+    private static final String REFACTORING_FILE = "Test.java";
+
+    private BugCheckerRefactoringTestHelper mRefactoringHelper;
+    private CompilationTestHelper mCompilationHelper;
+
+    @Before
+    public void setUp() {
+        mRefactoringHelper = BugCheckerRefactoringTestHelper.newInstance(
+                HideInCommentsChecker.class, HideInCommentsCheckerTest.class);
+        mCompilationHelper = CompilationTestHelper.newInstance(
+                HideInCommentsChecker.class, HideInCommentsCheckerTest.class);
+    }
+
+
+    @Test
+    public void refactorSingleLineComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  // Foo @hide",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorSingleLineComment_doesntAddUnnecessarySpace() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  // Foo @hide ",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorSingleLineBlockComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorMultiLineBlockComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /*",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /**",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorFieldComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  public int foo = 0;",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  public int foo = 0;",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorClassComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "/* Foo @hide */",
+                        "public class Test {}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "/** Foo @hide */",
+                        "public class Test {}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void refactorEnumComment() {
+        mRefactoringHelper
+                .addInputLines(
+                        REFACTORING_FILE,
+                        "public enum Test {",
+                        "  /* Foo @hide */",
+                        "  FOO",
+                        "}")
+                .addOutputLines(
+                        REFACTORING_FILE,
+                        "public enum Test {",
+                        "  /** Foo @hide */",
+                        "  FOO",
+                        "}")
+                .doTest(TEXT_MATCH);
+    }
+
+    @Test
+    public void canBeSuppressed() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /* Foo @hide */",
+                        "  @SuppressWarnings(\"AndroidHideInComments\")",
+                        "  void foo() {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void isInJavadoc() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /** Foo @hide */",
+                        "  void foo() {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void isInMultilineJavadoc() {
+        mCompilationHelper
+                .addSourceLines(
+                        REFACTORING_FILE,
+                        "public class Test {",
+                        "  /**",
+                        "   * Foo.",
+                        "   *",
+                        "   * @hide",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void noHidePresent() {
+        mCompilationHelper
+                .addSourceLines(
+                        "test/" + REFACTORING_FILE,
+                        "package test;",
+                        "// Foo.",
+                        "public class Test {",
+                        "  // Foo.",
+                        "  public int a;",
+                        "  /*",
+                        "   * Foo.",
+                        "   *",
+                        "   */",
+                        "  void foo(int foo) {}",
+                        "}")
+                .doTest();
+    }
+
+}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2f978fc..582488f 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -22,6 +22,10 @@
 import android.annotation.Nullable;
 import android.annotation.Size;
 import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
+import android.util.SparseIntArray;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -207,6 +211,7 @@
 
     // See static initialization block next to #get(Named)
     private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
+    private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();
 
     @NonNull private final String mName;
     @NonNull private final Model mModel;
@@ -1389,6 +1394,47 @@
     }
 
     /**
+     * Create a {@link ColorSpace} object using a {@link android.hardware.DataSpace DataSpace}
+     * value.
+     *
+     * <p>This function maps from a dataspace to a {@link Named} ColorSpace.
+     * If no {@link Named} ColorSpace object matching the {@code dataSpace} value can be created,
+     * {@code null} will return.</p>
+     *
+     * @param dataSpace The dataspace value
+     * @return the ColorSpace object or {@code null} if no matching colorspace can be found.
+     */
+    @SuppressLint("MethodNameUnits")
+    @Nullable
+    public static ColorSpace getFromDataSpace(@NamedDataSpace int dataSpace) {
+        int index = sDataToColorSpaces.get(dataSpace, -1);
+        if (index != -1) {
+            return ColorSpace.get(index);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Retrieve the {@link android.hardware.DataSpace DataSpace} value from a {@link ColorSpace}
+     * object.
+     *
+     * <p>If this {@link ColorSpace} object has no matching {@code dataSpace} value,
+     * {@link android.hardware.DataSpace#DATASPACE_UNKNOWN DATASPACE_UNKNOWN} will return.</p>
+     *
+     * @return the dataspace value.
+     */
+    @SuppressLint("MethodNameUnits")
+    public @NamedDataSpace int getDataSpace() {
+        int index = sDataToColorSpaces.indexOfValue(getId());
+        if (index != -1) {
+            return sDataToColorSpaces.keyAt(index);
+        } else {
+            return DataSpace.DATASPACE_UNKNOWN;
+        }
+    }
+
+    /**
      * <p>Returns an instance of {@link ColorSpace} identified by the specified
      * name. The list of names provided in the {@link Named} enum gives access
      * to a variety of common RGB color spaces.</p>
@@ -1445,6 +1491,7 @@
                 SRGB_TRANSFER_PARAMETERS,
                 Named.SRGB.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
         sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
                 "sRGB IEC61966-2.1 (Linear)",
                 SRGB_PRIMARIES,
@@ -1453,6 +1500,7 @@
                 0.0f, 1.0f,
                 Named.LINEAR_SRGB.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
         sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
                 "scRGB-nl IEC 61966-2-2:2003",
                 SRGB_PRIMARIES,
@@ -1464,6 +1512,7 @@
                 SRGB_TRANSFER_PARAMETERS,
                 Named.EXTENDED_SRGB.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
         sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
                 "scRGB IEC 61966-2-2:2003",
                 SRGB_PRIMARIES,
@@ -1472,6 +1521,8 @@
                 -0.5f, 7.499f,
                 Named.LINEAR_EXTENDED_SRGB.ordinal()
         );
+        sDataToColorSpaces.put(
+                DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
         sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
                 "Rec. ITU-R BT.709-5",
                 new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
@@ -1480,6 +1531,7 @@
                 new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
                 Named.BT709.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
         sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
                 "Rec. ITU-R BT.2020-1",
                 new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
@@ -1488,6 +1540,7 @@
                 new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
                 Named.BT2020.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
         sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
                 "SMPTE RP 431-2-2007 DCI (P3)",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
@@ -1496,6 +1549,7 @@
                 0.0f, 1.0f,
                 Named.DCI_P3.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
         sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
                 "Display P3",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
@@ -1504,6 +1558,7 @@
                 SRGB_TRANSFER_PARAMETERS,
                 Named.DISPLAY_P3.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
         sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
                 "NTSC (1953)",
                 NTSC_1953_PRIMARIES,
@@ -1528,6 +1583,7 @@
                 0.0f, 1.0f,
                 Named.ADOBE_RGB.ordinal()
         );
+        sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
         sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
                 "ROMM RGB ISO 22028-2:2013",
                 new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 9feb619..b341a4e 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -428,7 +428,7 @@
 
     /**
      * <p>Private raw camera sensor image format, a single channel image with
-     * implementation depedent pixel layout.</p>
+     * implementation dependent pixel layout.</p>
      *
      * <p>RAW_PRIVATE is a format for unprocessed raw image buffers coming from an
      * image sensor. The actual structure of buffers of this format is
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index cffdf28..d083e44 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -26,7 +26,7 @@
 /**
  * Indicates the strategies can be used when calculating the text wrapping.
  *
- * See <a href="https://drafts.csswg.org/css-text/#line-break-property">the line-break property</a>
+ * See <a href="https://www.w3.org/TR/css-text-3/#line-break-property">the line-break property</a>
  */
 public final class LineBreakConfig {
 
@@ -78,21 +78,87 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface LineBreakWordStyle {}
 
-    private @LineBreakStyle int mLineBreakStyle = LINE_BREAK_STYLE_NONE;
-    private @LineBreakWordStyle int mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_NONE;
+    /**
+     * A builder for creating {@link LineBreakConfig}.
+     */
+    public static final class Builder {
+        // The line break style for the LineBreakConfig.
+        private @LineBreakStyle int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE;
 
-    public LineBreakConfig() {
+        // The line break word style for the LineBreakConfig.
+        private @LineBreakWordStyle int mLineBreakWordStyle =
+                LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+
+        /**
+         * Builder constructor with line break parameters.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the line break style.
+         *
+         * @param lineBreakStyle the new line break style.
+         * @return this Builder
+         */
+        public @NonNull Builder setLineBreakStyle(@LineBreakStyle int lineBreakStyle) {
+            mLineBreakStyle = lineBreakStyle;
+            return this;
+        }
+
+        /**
+         * Set the line break word style.
+         *
+         * @param lineBreakWordStyle the new line break word style.
+         * @return this Builder
+         */
+        public @NonNull Builder setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) {
+            mLineBreakWordStyle = lineBreakWordStyle;
+            return this;
+        }
+
+        /**
+         * Build the {@link LineBreakConfig}
+         *
+         * @return the LineBreakConfig instance.
+         */
+        public @NonNull LineBreakConfig build() {
+            return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle);
+        }
     }
 
     /**
-     * Set the line break configuration.
+     * Create the LineBreakConfig instance.
      *
-     * @param lineBreakConfig the new line break configuration.
+     * @param lineBreakStyle the line break style for text wrapping.
+     * @param lineBreakWordStyle the line break word style for text wrapping.
+     * @return the {@link LineBreakConfig} instance.
+     * @hide
      */
-    public void set(@NonNull LineBreakConfig lineBreakConfig) {
-        Objects.requireNonNull(lineBreakConfig);
-        mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
-        mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
+    public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle,
+            @LineBreakWordStyle int lineBreakWordStyle) {
+        LineBreakConfig.Builder builder = new LineBreakConfig.Builder();
+        return builder.setLineBreakStyle(lineBreakStyle)
+                .setLineBreakWordStyle(lineBreakWordStyle)
+                .build();
+    }
+
+    /** @hide */
+    public static final LineBreakConfig NONE =
+            new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE)
+                    .setLineBreakWordStyle(LINE_BREAK_WORD_STYLE_NONE).build();
+
+    private final @LineBreakStyle int mLineBreakStyle;
+    private final @LineBreakWordStyle int mLineBreakWordStyle;
+
+    /**
+     * Constructor with the line break parameters.
+     * Use the {@link LineBreakConfig.Builder} to create the LineBreakConfig instance.
+     */
+    private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
+            @LineBreakWordStyle int lineBreakWordStyle) {
+        mLineBreakStyle = lineBreakStyle;
+        mLineBreakWordStyle = lineBreakWordStyle;
     }
 
     /**
@@ -105,15 +171,6 @@
     }
 
     /**
-     * Set the line break style.
-     *
-     * @param lineBreakStyle the new line break style.
-     */
-    public void setLineBreakStyle(@LineBreakStyle int lineBreakStyle) {
-        mLineBreakStyle = lineBreakStyle;
-    }
-
-    /**
      * Get the line break word style.
      *
      * @return The current line break word style to be used for the text wrapping.
@@ -122,15 +179,6 @@
         return mLineBreakWordStyle;
     }
 
-    /**
-     * Set the line break word style.
-     *
-     * @param lineBreakWordStyle the new line break word style.
-     */
-    public void setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) {
-        mLineBreakWordStyle = lineBreakWordStyle;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (o == null) return false;
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6d691c1..3f7f088 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -34,6 +34,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Result of text shaping of the single paragraph string.
@@ -56,18 +57,22 @@
 public class MeasuredText {
     private static final String TAG = "MeasuredText";
 
-    private long mNativePtr;
-    private boolean mComputeHyphenation;
-    private boolean mComputeLayout;
-    private @NonNull char[] mChars;
+    private final long mNativePtr;
+    private final boolean mComputeHyphenation;
+    private final boolean mComputeLayout;
+    @NonNull private final char[] mChars;
+    private final int mTop;
+    private final int mBottom;
 
     // Use builder instead.
     private MeasuredText(long ptr, @NonNull char[] chars, boolean computeHyphenation,
-            boolean computeLayout) {
+            boolean computeLayout, int top, int bottom) {
         mNativePtr = ptr;
         mChars = chars;
         mComputeHyphenation = computeHyphenation;
         mComputeLayout = computeLayout;
+        mTop = top;
+        mBottom = bottom;
     }
 
     /**
@@ -124,6 +129,30 @@
     }
 
     /**
+     * Retrieves the font metrics of the given range
+     *
+     * @param start an inclusive start index of the range
+     * @param end an exclusive end index of the range
+     * @param outMetrics an output metrics object
+     */
+    public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Paint.FontMetricsInt outMetrics) {
+        Preconditions.checkArgument(0 <= start && start <= mChars.length,
+                "start(%d) must be 0 <= start <= %d", start, mChars.length);
+        Preconditions.checkArgument(0 <= end && end <= mChars.length,
+                "end(%d) must be 0 <= end <= %d", end, mChars.length);
+        Preconditions.checkArgument(start <= end,
+                "start(%d) is larger than end(%d)", start, end);
+        Objects.requireNonNull(outMetrics);
+
+        long packed = nGetExtent(mNativePtr, mChars, start, end);
+        outMetrics.ascent = (int) (packed >> 32);
+        outMetrics.descent = (int) (packed & 0xFFFFFFFF);
+        outMetrics.top = Math.min(outMetrics.ascent, mTop);
+        outMetrics.bottom = Math.max(outMetrics.descent, mBottom);
+    }
+
+    /**
      * Returns the width of the character at the given offset.
      *
      * @param offset an offset of the character.
@@ -160,6 +189,8 @@
     @CriticalNative
     private static native float nGetCharWidthAt(long nativePtr, int offset);
 
+    private static native long nGetExtent(long nativePtr, char[] buf, int start, int end);
+
     /**
      * Helper class for creating a {@link MeasuredText}.
      * <p>
@@ -189,6 +220,9 @@
         private boolean mFastHyphenation = false;
         private int mCurrentOffset = 0;
         private @Nullable MeasuredText mHintMt = null;
+        private int mTop = 0;
+        private int mBottom = 0;
+        private Paint.FontMetricsInt mCachedMetrics = new Paint.FontMetricsInt();
 
         /**
          * Construct a builder.
@@ -269,6 +303,10 @@
             nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
                     mCurrentOffset, end, isRtl);
             mCurrentOffset = end;
+
+            paint.getFontMetricsInt(mCachedMetrics);
+            mTop = Math.min(mTop, mCachedMetrics.top);
+            mBottom = Math.max(mBottom, mCachedMetrics.bottom);
             return this;
         }
 
@@ -419,7 +457,7 @@
                 long ptr = nBuildMeasuredText(mNativePtr, hintPtr, mText, mComputeHyphenation,
                         mComputeLayout, mFastHyphenation);
                 final MeasuredText res = new MeasuredText(ptr, mText, mComputeHyphenation,
-                        mComputeLayout);
+                        mComputeLayout, mTop, mBottom);
                 sRegistry.registerNativeAllocation(res, ptr);
                 return res;
             } finally {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index d35ecbd..0bf078d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -833,8 +833,8 @@
 
             if (shouldExpand(null, intent, getSplitRules())) {
                 setLaunchingInExpandedContainer(launchingActivity, options);
-            } else if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
-                setLaunchingInSameContainer(launchingActivity, intent, options);
+            } else if (!splitWithLaunchingActivity(launchingActivity, intent, options)) {
+                setLaunchingInSameSideContainer(launchingActivity, intent, options);
             }
 
             return super.onStartActivity(who, intent, options);
@@ -853,9 +853,9 @@
         /**
          * Returns {@code true} if the activity that is going to be started via the
          * {@code intent} should be paired with the {@code launchingActivity} and is set to be
-         * launched in an empty side container.
+         * launched in the side container.
          */
-        private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent,
+        private boolean splitWithLaunchingActivity(Activity launchingActivity, Intent intent,
                 Bundle options) {
             final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent,
                     getSplitRules());
@@ -863,9 +863,14 @@
                 return false;
             }
 
-            // Create a new split with an empty side container
-            final TaskFragmentContainer secondaryContainer = mPresenter
-                    .createNewSplitWithEmptySideContainer(launchingActivity, splitPairRule);
+            // Check if there is any existing side container to launch into.
+            TaskFragmentContainer secondaryContainer = findSideContainerForNewLaunch(
+                    launchingActivity, splitPairRule);
+            if (secondaryContainer == null) {
+                // Create a new split with an empty side container.
+                secondaryContainer = mPresenter
+                        .createNewSplitWithEmptySideContainer(launchingActivity, splitPairRule);
+            }
 
             // Amend the request to let the WM know that the activity should be placed in the
             // dedicated container.
@@ -875,12 +880,39 @@
         }
 
         /**
+         * Finds if there is an existing split side {@link TaskFragmentContainer} that can be used
+         * for the new rule.
+         */
+        @Nullable
+        private TaskFragmentContainer findSideContainerForNewLaunch(Activity launchingActivity,
+                SplitPairRule splitPairRule) {
+            final TaskFragmentContainer launchingContainer = getContainerWithActivity(
+                    launchingActivity.getActivityToken());
+            if (launchingContainer == null) {
+                return null;
+            }
+
+            // We only check if the launching activity is the primary of the split. We will check
+            // if the launching activity is the secondary in #setLaunchingInSameSideContainer.
+            final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer);
+            if (splitContainer == null
+                    || splitContainer.getPrimaryContainer() != launchingContainer) {
+                return null;
+            }
+
+            if (canReuseContainer(splitPairRule, splitContainer.getSplitRule())) {
+                return splitContainer.getSecondaryContainer();
+            }
+            return null;
+        }
+
+        /**
          * Checks if the activity that is going to be started via the {@code intent} should be
          * paired with the existing top activity which is currently paired with the
-         * {@code launchingActivity}. If so, set the activity to be launched in the same
+         * {@code launchingActivity}. If so, set the activity to be launched in the same side
          * container of the {@code launchingActivity}.
          */
-        private void setLaunchingInSameContainer(Activity launchingActivity, Intent intent,
+        private void setLaunchingInSameSideContainer(Activity launchingActivity, Intent intent,
                 Bundle options) {
             final TaskFragmentContainer launchingContainer = getContainerWithActivity(
                     launchingActivity.getActivityToken());
@@ -911,6 +943,11 @@
                 return;
             }
 
+            // Can only launch in the same container if the rules share the same presentation.
+            if (!canReuseContainer(splitPairRule, splitContainer.getSplitRule())) {
+                return;
+            }
+
             // Amend the request to let the WM know that the activity should be placed in the
             // dedicated container. This is necessary for the case that the activity is started
             // into a new Task, or new Task will be escaped from the current host Task and be
@@ -928,4 +965,31 @@
     public boolean isActivityEmbedded(@NonNull Activity activity) {
         return mPresenter.isActivityEmbedded(activity.getActivityToken());
     }
+
+    /**
+     * If the two rules have the same presentation, we can reuse the same {@link SplitContainer} if
+     * there is any.
+     */
+    private static boolean canReuseContainer(SplitRule rule1, SplitRule rule2) {
+        if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) {
+            return false;
+        }
+        return rule1.getSplitRatio() == rule2.getSplitRatio()
+                && rule1.getLayoutDirection() == rule2.getLayoutDirection();
+    }
+
+    /**
+     * Whether it is ok for other rule to reuse the {@link TaskFragmentContainer} of the given
+     * rule.
+     */
+    private static boolean isContainerReusableRule(SplitRule rule) {
+        // We don't expect to reuse the placeholder rule.
+        if (!(rule instanceof SplitPairRule)) {
+            return false;
+        }
+        final SplitPairRule pairRule = (SplitPairRule) rule;
+
+        // Not reuse if it needs to destroy the existing.
+        return !pairRule.shouldClearTop();
+    }
 }
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 88382d7..2476f65 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige programme werk beter in portret"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van hierdie opsies om jou spasie ten beste te benut"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai jou toestel om dit volskerm te maak"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik langs ’n program om dit te herposisioneer"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index 1bfe128..3a2bc1e 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
     <string name="pip_move" msgid="1544227837964635439">"Skuif PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 20d081f..f0c391c 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"አንዳንድ መተግበሪያዎች በቁም ፎቶ ውስጥ በተሻለ ሁኔታ ይሰራሉ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ቦታዎን በአግባቡ ለመጠቀም ከእነዚህ አማራጮች ውስጥ አንዱን ይሞክሩ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ወደ የሙሉ ገጽ ዕይታ ለመሄድ መሣሪያዎን ያሽከርክሩት"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ቦታውን ለመቀየር ከመተግበሪያው ቀጥሎ ላይ ሁለቴ መታ ያድርጉ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index 456b4b8..23a3394 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
     <string name="pip_move" msgid="1544227837964635439">"ፒአይፒ ውሰድ"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index b41e642..aa4b3b7 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"تعمل بعض التطبيقات على أكمل وجه في الشاشات العمودية"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"جرِّب تنفيذ أحد هذه الخيارات للاستفادة من مساحتك إلى أقصى حد."</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"قم بتدوير الشاشة للانتقال إلى وضع ملء الشاشة."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"انقر مرتين بجانب التطبيق لتغيير موضعه."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"حسنًا"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
index 2546fe9..f64528d 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
     <string name="pip_move" msgid="1544227837964635439">"‏نقل نافذة داخل النافذة (PIP)"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 663691f..985d3b9 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"কিছুমান এপে প’ৰ্ট্ৰেইট ম’ডত বেছি ভালকৈ কাম কৰে"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপোনাৰ spaceৰ পৰা পাৰ্যমানে উপকৃত হ’বলৈ ইয়াৰে এটা বিকল্প চেষ্টা কৰি চাওক"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"পূৰ্ণ স্ক্ৰীনলৈ যাবলৈ আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"এপ্‌টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ কাষত দুবাৰ টিপক"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"বুজি পালোঁ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index d17c1f3..67d27a7 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
     <string name="pip_move" msgid="1544227837964635439">"পিপ স্থানান্তৰ কৰক"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 646aba8..8cd9b7a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bəzi tətbiqlər portret rejimində daha yaxşı işləyir"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Məkanınızdan maksimum yararlanmaq üçün bu seçimlərdən birini sınayın"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana keçmək üçün cihazınızı fırladın"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tətbiqin yerini dəyişmək üçün yanına iki dəfə toxunun"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index a5c4792..670b02f 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP tətbiq edin"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"PIP-ni genişləndirin"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"PIP-ni yığcamlaşdırın"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 2ebdf92..49524c6 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcionišu u uspravnom režimu"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste na najbolji način iskoristili prostor"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotirajte uređaj za prikaz preko celog ekrana"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste promenili njenu poziciju"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index b4d9bd1..9bc22eb 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"Premesti sliku u slici"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 157e168..1767e0d 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некаторыя праграмы лепш за ўсё працуюць у кніжнай арыентацыі"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Каб эфектыўна выкарыстоўваць прастору, паспрабуйце адзін з гэтых варыянтаў"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Каб перайсці ў поўнаэкранны рэжым, павярніце прыладу"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двойчы націсніце побач з праграмай, каб перамясціць яе"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Зразумела"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index 514d06b..a85232d 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
     <string name="pip_move" msgid="1544227837964635439">"Перамясціць PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 4ed8672..c22fb86 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Някои приложения работят най-добре във вертикален режим"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Изпробвайте една от следните опции, за да се възползвате максимално от мястото на екрана"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Завъртете екрана си, за да преминете в режим на цял екран"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Докоснете два пъти дадено приложение, за да промените позицията му"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
index 19f83e7..961489f 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
     <string name="pip_move" msgid="1544227837964635439">"„Картина в картина“: Преместв."</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 7579fac..c0944e05 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"কিছু অ্যাপ \'পোর্ট্রেট\' মোডে সবচেয়ে ভাল কাজ করে"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"আপনার স্পেস সবচেয়ে ভালভাবে কাজে লাগাতে এইসব বিকল্পের মধ্যে কোনও একটি ব্যবহার করে দেখুন"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"\'ফুল স্ক্রিন\' মোডে যেতে ডিভাইস ঘোরান"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"কোনও অ্যাপের পাশে ডবল ট্যাপ করে সেটির জায়গা পরিবর্তন করুন"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"বুঝেছি"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
index 5f90eeb..a8880dc 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP সরান"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 7b08d03..ae01c64 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Određene aplikacije najbolje funkcioniraju u uspravnom načinu rada"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da maksimalno iskoristite prostor"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zarotirajte uređaj da aktivirate prikaz preko cijelog ekrana"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da promijenite njen položaj"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
index 3f2adf3..654ab69 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zatvori sliku u slici"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"Pokreni sliku u slici"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 44429cc..2ec1db4 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunes aplicacions funcionen millor en posició vertical"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una d\'aquestes opcions per treure el màxim profit de l\'espai"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositiu per passar a pantalla completa"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Fes doble toc al costat d\'una aplicació per canviar-ne la posició"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index db750c4..43cc070 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mou pantalla en pantalla"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index d6e7136..d0cf80a 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Některé aplikace fungují nejlépe na výšku"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pokud chcete maximálně využít prostor, vyzkoušejte jednu z těchto možností"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zařízení přejděte do režimu celé obrazovky"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedle aplikace změňte její umístění"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
index cef0b99..772d77e 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
     <string name="pip_move" msgid="1544227837964635439">"Přesunout PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index e7b8e73..bb81c10 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nogle apps fungerer bedst i stående format"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv én af disse muligheder for at få mest muligt ud af dit rum"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Drej din enhed for at gå til fuld skærm"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryk to gange ud for en app for at ændre dens placering"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
index 2330530..2f3b359 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
     <string name="pip_move" msgid="1544227837964635439">"Flyt PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Udvid PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Skjul PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 57af696c..3a110793 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Einige Apps funktionieren am besten im Hochformat"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Mithilfe dieser Möglichkeiten kannst du dein Display optimal nutzen"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gerät drehen, um zum Vollbildmodus zu wechseln"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Neben einer App doppeltippen, um die Position zu ändern"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 8da9110..3305a21 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
     <string name="pip_move" msgid="1544227837964635439">"BiB verschieben"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 873b329..70f5505 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Ορισμένες εφαρμογές λειτουργούν καλύτερα σε κατακόρυφο προσανατολισμό"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Δοκιμάστε μία από αυτές τις επιλογές για να αξιοποιήσετε στο έπακρο τον χώρο σας."</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Περιστρέψτε τη συσκευή σας για μετάβαση σε πλήρη οθόνη."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Πατήστε δύο φορές δίπλα σε μια εφαρμογή για να αλλάξετε τη θέση της."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατάλαβα"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
index df35113..cf6e077 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
     <string name="pip_move" msgid="1544227837964635439">"Μετακίνηση PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index da4933b..0b5aefa5 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
index 1fb3191..79a8b95 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
     <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index da4933b..0b5aefa5 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
index 1fb3191..79a8b95 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
     <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index da4933b..0b5aefa5 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
index 1fb3191..79a8b95 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
     <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index da4933b..0b5aefa5 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Some apps work best in portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Try one of these options to make the most of your space"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
index 1fb3191..79a8b95 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
     <string name="pip_move" msgid="1544227837964635439">"Move PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Expand PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Collapse PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
index 3b12d90..8925f18 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎Close PIP‎‏‎‎‏‎"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎Full screen‎‏‎‎‏‎"</string>
     <string name="pip_move" msgid="1544227837964635439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎Move PIP‎‏‎‎‏‎"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎Expand PIP‎‏‎‎‏‎"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎Collapse PIP‎‏‎‎‏‎"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 154c7ab..e523ae5 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas apps funcionan mejor en modo vertical"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba estas opciones para aprovechar al máximo tu espacio"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rota el dispositivo para ver la pantalla completa"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Presiona dos veces junto a una app para cambiar su posición"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index 1beb0b5..f901c63 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index e2fa3a0..9749607 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunas aplicaciones funcionan mejor en vertical"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prueba una de estas opciones para sacar el máximo partido al espacio de tu pantalla"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositivo para ir al modo de pantalla completa"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dos veces junto a una aplicación para cambiar su posición"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index d042b43..3aec3a7 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover imagen en imagen"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index da33f4d..a5f82a6 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Mõni rakendus töötab kõige paremini vertikaalpaigutuses"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proovige ühte neist valikutest, et oma ruumi parimal moel kasutada"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pöörake seadet, et aktiveerida täisekraanirežiim"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Topeltpuudutage rakenduse kõrval, et selle asendit muuta"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
index 3da16db..51f8dfd 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
     <string name="pip_move" msgid="1544227837964635439">"Teisalda PIP-režiimi"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index e0dd3ca2..caa335a 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Aplikazio batzuk orientazio bertikalean funtzionatzen dute hobekien"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pantailako eremuari ahalik eta etekinik handiena ateratzeko, probatu aukera hauetakoren bat"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pantaila osoko modua erabiltzeko, biratu gailua"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren ondoko edozein toki"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
index e4b57ba..b718791 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mugitu pantaila txiki gainjarria"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 6fcb5ee..9e72571 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"برخی‌از برنامه‌ها در حالت عمودی عملکرد بهتری دارند"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"با امتحان کردن یکی از این گزینه‌ها، بیشترین بهره را از فضایتان ببرید"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"برای رفتن به حالت تمام صفحه، دستگاهتان را بچرخانید"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"در کنار برنامه دوضربه بزنید تا جابه‌جا شود"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجه‌ام"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index aaab34f..9fca855 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
     <string name="pip_move" msgid="1544227837964635439">"‏انتقال PIP (تصویر در تصویر)"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fc51ad4..c809b487 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Osa sovelluksista toimii parhaiten pystytilassa"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Kokeile jotakin näistä vaihtoehdoista, jotta saat parhaan hyödyn näytön tilasta"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Käännä laitetta, niin se siirtyy koko näytön tilaan"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kaksoisnapauta sovellusta, jos haluat siirtää sitä"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
index 21c6463..b40faf1 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
     <string name="pip_move" msgid="1544227837964635439">"Siirrä PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 43fad3a..62b2bb6 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applications fonctionnent mieux en mode portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour tirer le meilleur parti de votre espace"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter votre appareil pour passer en plein écran"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
index f4baaad..c1d8bb50 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
     <string name="pip_move" msgid="1544227837964635439">"Déplacer l\'image incrustée"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 8b8cc09..b3e22af 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Certaines applis fonctionnent mieux en mode Portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Essayez l\'une de ces options pour exploiter pleinement l\'espace"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter l\'appareil pour passer en plein écran"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Appuyez deux fois à côté d\'une appli pour la repositionner"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
index 6ad8174..969446e 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Fermer mode PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
     <string name="pip_move" msgid="1544227837964635439">"Déplacer le PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 9bc9d93..b8e0396 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algunhas aplicacións funcionan mellor en modo vertical"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Proba unha destas opcións para sacar o máximo proveito do espazo da pantalla"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xira o dispositivo para ver o contido en pantalla completa"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dúas veces a carón dunha aplicación para cambiala de posición"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
index dcb8709..3a3d9d6 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover pantalla superposta"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 032b591..deda2d7 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"અમુક ઍપ પોર્ટ્રેટ મોડમાં શ્રેષ્ઠ રીતે કાર્ય કરે છે"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"તમારી સ્પેસનો વધુને વધુ લાભ લેવા માટે, આ વિકલ્પોમાંથી કોઈ એક અજમાવો"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"પૂર્ણ સ્ક્રીન મોડ લાગુ કરવા માટે, તમારા ડિવાઇસને ફેરવો"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બાજુમાં બે વાર ટૅપ કરો"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"સમજાઈ ગયું"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index ed815ca..0851127 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP ખસેડો"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 72fd65c..36b1151 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"कुछ ऐप्लिकेशन, पोर्ट्रेट मोड में सबसे अच्छी तरह काम करते हैं"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"जगह का पूरा इस्तेमाल करने के लिए, इनमें से किसी एक विकल्प को आज़माएं"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फ़ुल स्क्रीन मोड में जाने के लिए, डिवाइस को घुमाएं"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बगल में दो बार टैप करें"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
index 8bcc631..f94f485 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
     <string name="pip_move" msgid="1544227837964635439">"पीआईपी को दूसरी जगह लेकर जाएं"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 5315558..5ecc558 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Neke aplikacije najbolje funkcioniraju u portretnom usmjerenju"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Isprobajte jednu od ovih opcija da biste maksimalno iskoristili prostor"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zakrenite uređaj radi prikaza na cijelom zaslonu"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste joj promijenili položaj"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
index 49b7ae0..ef2f7ca 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
     <string name="pip_move" msgid="1544227837964635439">"Premjesti PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 01671c9..2295250 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Egyes alkalmazások álló tájolásban működnek a leghatékonyabban"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Próbálja ki az alábbi beállítások egyikét, hogy a legjobban ki tudja használni képernyő területét"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"A teljes képernyős mód elindításához forgassa el az eszközt"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Koppintson duplán az alkalmazás mellett az áthelyezéséhez"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index 484db0c..02d86fa 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP áthelyezése"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 459cd0a..2089365 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Որոշ հավելվածներ լավագույնս աշխատում են դիմանկարի ռեժիմում"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Փորձեք այս տարբերակներից մեկը՝ տարածքը հնարավորինս արդյունավետ օգտագործելու համար"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Պտտեք սարքը՝ լիաէկրան ռեժիմին անցնելու համար"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Եղավ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
index e447ffc..0a040e7 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
     <string name="pip_move" msgid="1544227837964635439">"Տեղափոխել PIP-ը"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index e5b7421..1b46b2f 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Beberapa aplikasi berfungsi paling baik dalam mode potret"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Coba salah satu opsi berikut untuk mengoptimalkan area layar Anda"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar perangkat untuk tampilan layar penuh"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketuk dua kali di samping aplikasi untuk mengubah posisinya"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
index b631705..89d8929 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
     <string name="pip_move" msgid="1544227837964635439">"Pindahkan PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 1bfec2b..a201c95 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sum forrit virka best í skammsniði"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prófaðu einhvern af eftirfarandi valkostum til að nýta plássið sem best"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Snúðu tækinu til að nota allan skjáinn"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ýttu tvisvar við hlið forritsins til að færa það"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
index 119ecf0..6264927 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
     <string name="pip_move" msgid="1544227837964635439">"Færa innfellda mynd"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index ebdf44b..dd5416f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alcune app funzionano in modo ottimale in verticale"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prova una di queste opzioni per ottimizzare lo spazio a tua disposizione"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ruota il dispositivo per passare alla modalità a schermo intero"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tocca due volte accanto a un\'app per riposizionarla"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index 92f015c..638da38 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
     <string name="pip_move" msgid="1544227837964635439">"Sposta PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 3a0f72b..52a6b06 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"חלק מהאפליקציות פועלות בצורה הטובה ביותר במצב תצוגה לאורך"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"כדי להפיק את המרב משטח המסך, ניתן לנסות את אחת מהאפשרויות האלה"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"מסובבים את המכשיר כדי לעבור לתצוגה במסך מלא"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"מקישים הקשה כפולה ליד אפליקציה כדי למקם אותה מחדש"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"הבנתי"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index d09b850..906c69b 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
     <string name="pip_move" msgid="1544227837964635439">"‏העברת תמונה בתוך תמונה (PIP)"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 7b3ad24..5a25c24 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"アプリによっては縦向きにすると正常に動作します"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"スペースを最大限に活用するには、以下の方法のいずれかをお試しください"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"全画面表示にするにはデバイスを回転させてください"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"位置を変えるにはアプリの横をダブルタップしてください"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index d6399e5..9b166e0 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP を移動"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 07ee0f9..bff86fa 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ზოგიერთი აპი უკეთ მუშაობს პორტრეტის რეჟიმში"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"გამოცადეთ ამ ვარიანტებიდან ერთ-ერთი, რათა მაქსიმალურად ისარგებლოთ თქვენი მეხსიერებით"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"მოატრიალეთ თქვენი მოწყობილობა სრული ეკრანის გასაშლელად"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ორმაგად შეეხეთ აპის გვერდითა სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index 8d7bee8..025e5a5 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP გადატანა"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"PIP-ის გაშლა"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"PIP-ის ჩაკეცვა"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index bdaa03e..f57f3f5 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Кейбір қолданба портреттік режимде жақсы жұмыс істейді"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Экранды тиімді пайдалану үшін мына опциялардың бірін байқап көріңіз."</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толық экранға ауысу үшін құрылғыңызды бұрыңыз."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Қолданбаның орнын ауыстыру үшін жанынан екі рет түртіңіз."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Түсінікті"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index 05bdcc7..5ad60ef 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP клипін жылжыту"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 2654765..5c04f88 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"កម្មវិធីមួយចំនួនដំណើរការបានប្រសើរបំផុតក្នុងទិសដៅបញ្ឈរ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"សាកល្បងជម្រើសមួយក្នុងចំណោមទាំងនេះ ដើម្បីទទួលបានអត្ថប្រយោជន៍ច្រើនបំផុតពីកន្លែងទំនេររបស់អ្នក"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"បង្វិលឧបករណ៍របស់អ្នក ដើម្បីចូលប្រើអេក្រង់ពេញ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ចុចពីរដងនៅជាប់កម្មវិធីណាមួយ ដើម្បីប្ដូរទីតាំងកម្មវិធីនោះ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហើយ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
index e831516..1e99507 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
     <string name="pip_move" msgid="1544227837964635439">"ផ្លាស់ទី PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 6edbf13..e91383c 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ಕೆಲವು ಆ್ಯಪ್‌ಗಳು ಪೋರ್ಟ್ರೇಟ್ ಮೋಡ್‌ನಲ್ಲಿ ಅತ್ಯುತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತವೆ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ನಿಮ್ಮ ಸ್ಥಳಾವಕಾಶದ ಅತಿಹೆಚ್ಚು ಪ್ರಯೋಜನ ಪಡೆಯಲು ಈ ಆಯ್ಕೆಗಳಲ್ಲಿ ಒಂದನ್ನು ಬಳಸಿ ನೋಡಿ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಪಕ್ಕದಲ್ಲಿ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ಸರಿ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 305ef66..bbf422d 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP ಅನ್ನು ಸರಿಸಿ"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 1f8d0b0..104ba3f 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"일부 앱은 세로 모드에서 가장 잘 작동함"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"공간을 최대한 이용할 수 있도록 이 옵션 중 하나를 시도해 보세요."</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"전체 화면 모드로 전환하려면 기기를 회전하세요."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"앱 위치를 조정하려면 앱 옆을 두 번 탭하세요."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
index 76b0adf..fe00f08 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP 이동"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 81eb2d7..8203622 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Айрым колдонмолорду тигинен иштетүү туура болот"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Иш чөйрөсүнүн бардык мүмкүнчүлүктөрүн пайдалануу үчүн бул параметрлердин бирин колдонуп көрүңүз"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толук экран режимине өтүү үчүн түзмөктү буруңуз"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Колдонмонун ракурсун өзгөртүү үчүн анын тушуна эки жолу басыңыз"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Түшүндүм"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
index 57b955a..6e35bc7 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP\'ти жылдыруу"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 3252130..2439678 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ແອັບບາງຢ່າງເຮັດວຽກໄດ້ດີທີ່ສຸດໃນໂໝດລວງຕັ້ງ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ໃຫ້ລອງຕົວເລືອກໃດໜຶ່ງເຫຼົ່ານີ້ເພື່ອໃຊ້ປະໂຫຍດຈາກພື້ນທີ່ຂອງທ່ານໃຫ້ໄດ້ສູງສຸດ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ໝຸນອຸປະກອນຂອງທ່ານເພື່ອໃຊ້ແບບເຕັມຈໍ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ແຕະສອງເທື່ອໃສ່ຖັດຈາກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ເຂົ້າໃຈແລ້ວ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
index cbea84e..17baf91 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
     <string name="pip_move" msgid="1544227837964635439">"ຍ້າຍ PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 70654c7..e2ae643 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Kai kurios programos geriausiai veikia stačiuoju režimu"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Pabandykite naudoti vieną iš šių parinkčių, kad išnaudotumėte visą vietą"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pasukite įrenginį, kad įjungtumėte viso ekrano režimą"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dukart palieskite šalia programos, kad pakeistumėte jos poziciją"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index 81716a6..f907055 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
     <string name="pip_move" msgid="1544227837964635439">"Perkelti PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Iškleisti PIP"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Sutraukti PIP"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 74d1b3f..a77160b 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Dažas lietotnes vislabāk darbojas portreta režīmā"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Izmēģiniet vienu no šīm iespējām, lai efektīvi izmantotu pieejamo vietu"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pagrieziet ierīci, lai aktivizētu pilnekrāna režīmu"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Veiciet dubultskārienu blakus lietotnei, lai manītu tās pozīciju"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
index 5295cd2..52cdae3 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
     <string name="pip_move" msgid="1544227837964635439">"Pārvietot attēlu attēlā"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index be6ed4d..bac0c9e 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некои апликации најдобро работат во режим на портрет"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте една од опцииве за да го извлечете максимумот од вашиот простор"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте го уредот за да отворите на цел екран"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Допрете двапати до некоја апликација за да ја преместите"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Сфатив"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index fa48a6c..e9ee1388 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
     <string name="pip_move" msgid="1544227837964635439">"Премести PIP"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Прошири ја сликата во слика"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Собери ја сликата во слика"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 14a341b..de0f837 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ചില ആപ്പുകൾ പോർട്രെയ്റ്റിൽ മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്നു"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"നിങ്ങളുടെ ഇടം പരമാവധി പ്രയോജനപ്പെടുത്താൻ ഈ ഓപ്ഷനുകളിലൊന്ന് പരീക്ഷിക്കുക"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ഈ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ഒരു ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ, അതിന് തൊട്ടടുത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"മനസ്സിലായി"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index 5333757..1ed6b6e 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP നീക്കുക"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"PIP വികസിപ്പിക്കുക"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"PIP ചുരുക്കുക"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index b59f282..1205306 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Зарим апп нь босоо чиглэлд хамгийн сайн ажилладаг"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Орон зайгаа сайтар ашиглахын тулд эдгээр сонголтуудын аль нэгийг туршиж үзээрэй"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Төхөөрөмжөө бүтэн дэлгэцээр үзэхийн тулд эргүүлнэ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Аппыг дахин байрлуулахын тулд хажууд нь хоёр товшино"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ойлголоо"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index ca1d27f..d4a6942 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP-г зөөх"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"PIP-г дэлгэх"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"PIP-г хураах"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 3d2d6a3..c91d06f 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"काही ॲप्स पोर्ट्रेटमध्ये सर्वोत्तम काम करतात"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तुमच्या स्पेसचा पुरेपूर वापर करण्यासाठी, यांपैकी एक पर्याय वापरून पहा"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फुल स्क्रीन करण्यासाठी, तुमचे डिव्हाइस फिरवा"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या शेजारी दोनदा टॅप करा"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"समजले"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index 212bd21..940f983 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP हलवा"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"PIP चा विस्तार करा"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"PIP कोलॅप्स करा"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 4e9a7e9..652a991 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sesetengah apl berfungsi paling baik dalam mod potret"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Cuba salah satu daripada pilihan ini untuk memanfaatkan ruang anda sepenuhnya"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar peranti anda untuk beralih ke skrin penuh"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketik dua kali bersebelahan apl untuk menempatkan semula apl"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index ce29126..f6fca3b 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
     <string name="pip_move" msgid="1544227837964635439">"Alihkan PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 449e502..15d182c 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"အချို့အက်ပ်များသည် ဒေါင်လိုက်တွင် အကောင်းဆုံးလုပ်ဆောင်သည်"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"သင့်နေရာကို အကောင်းဆုံးအသုံးပြုနိုင်ရန် ဤရွေးစရာများထဲမှ တစ်ခုကို စမ်းကြည့်ပါ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ဖန်သားပြင်အပြည့်လုပ်ရန် သင့်စက်ကို လှည့်နိုင်သည်"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"အက်ပ်နေရာပြန်ချရန် ၎င်းဘေးတွင် နှစ်ချက်တို့နိုင်သည်"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
index 4847742..c7c0894 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP ရွှေ့ရန်"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 2172cc5..9fd42b2 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Noen apper fungerer best i stående format"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Prøv et av disse alternativene for å få mest mulig ut av plassen din"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Roter enheten for å starte fullskjerm"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dobbelttrykk ved siden av en app for å flytte den"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
index 7cef11c..83222bf 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
     <string name="pip_move" msgid="1544227837964635439">"Flytt BIB"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index ff01dcd..8dfec88 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"केही एपहरूले पोर्ट्रेटमा राम्रोसँग काम गर्छन्"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"तपाईं स्क्रिनको अधिकतम ठाउँ प्रयोग गर्न चाहनुहुन्छ भने यीमध्ये कुनै विकल्प प्रयोग गरी हेर्नुहोस्"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"तपाईं फुल स्क्रिन मोड हेर्न चाहनुहुन्छ भने आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको छेउमा डबल ट्याप गर्नुहोस्"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"बुझेँ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index 684d114..ce21a09 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP सार्नुहोस्"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 428cb3f..8468b04 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Sommige apps werken het best in de staande stand"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Probeer een van deze opties om optimaal gebruik te maken van je ruimte"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai je apparaat om naar volledig scherm te schakelen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index 8562517..ebc873a 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
     <string name="pip_move" msgid="1544227837964635439">"SIS verplaatsen"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index f9668a1..a8d8448 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"କିଛି ଆପ ପୋର୍ଟ୍ରେଟରେ ସବୁଠାରୁ ଭଲ କାମ କରେ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ଆପଣଙ୍କ ସ୍ପେସରୁ ଅଧିକ ଲାଭ ପାଇବାକୁ ଏହି ବିକଳ୍ପଗୁଡ଼ିକ ମଧ୍ୟରୁ ଗୋଟିଏ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ପୂର୍ଣ୍ଣ-ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହା ପାଖରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ବୁଝିଗଲି"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
index f8bc016..98745c6 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIPକୁ ମୁଭ କରନ୍ତୁ"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 7132597..f99176c 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"ਕੁਝ ਐਪਾਂ ਪੋਰਟਰੇਟ ਵਿੱਚ ਬਿਹਤਰ ਕੰਮ ਕਰਦੀਆਂ ਹਨ"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ਆਪਣੀ ਜਗ੍ਹਾ ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਲਾਹਾ ਲੈਣ ਲਈ ਇਨ੍ਹਾਂ ਵਿਕਲਪਾਂ ਵਿੱਚੋਂ ਕੋਈ ਇੱਕ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁਮਾਓ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਅੱਗੇ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ਸਮਝ ਲਿਆ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index 1667e5f..793b878 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP ਨੂੰ ਲਿਜਾਓ"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index f7f97ef..f2147c0 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektóre aplikacje działają najlepiej w orientacji pionowej"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Wypróbuj jedną z tych opcji, aby jak najlepiej wykorzystać miejsce"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Obróć urządzenie, aby przejść do pełnego ekranu"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kliknij dwukrotnie obok aplikacji, aby ją przenieść"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
index 28bf66a..06988e3 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"Przenieś PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index a3d2ab0..2efc554 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index 27626b8..28b977a 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 86872c8..c68a693 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Algumas apps funcionam melhor no modo vertical"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Experimente uma destas opções para aproveitar ao máximo o seu espaço"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rode o dispositivo para ficar em ecrã inteiro"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes junto a uma app para a reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index a2010ce..6c1fa59 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover Ecrã no ecrã"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"Expandir Ecrã no ecrã"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"Reduzir Ecrã no ecrã"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index a3d2ab0..2efc554 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Alguns apps funcionam melhor em modo retrato"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Tente uma destas opções para aproveitar seu espaço ao máximo"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index 27626b8..28b977a 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 5448e45..804d34f 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Unele aplicații funcționează cel mai bine în orientarea portret"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Încercați una dintre aceste opțiuni pentru a profita din plin de spațiu"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotiți dispozitivul pentru a trece în modul ecran complet"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Atingeți de două ori lângă o aplicație pentru a o repoziționa"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index 18e29a6..0f8546e 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Închideți PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
     <string name="pip_move" msgid="1544227837964635439">"Mutați fereastra PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 64e74a2..95bf1cf 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Некоторые приложения лучше работают в вертикальном режиме"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Чтобы эффективно использовать экранное пространство, выполните одно из следующих действий:"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Чтобы перейти в полноэкранный режим, поверните устройство."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Чтобы переместить приложение, нажмите на него дважды."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index d119240..a128b10 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
     <string name="pip_move" msgid="1544227837964635439">"Переместить PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 3cdaa72..23dd65a 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"සමහර යෙදුම් ප්‍රතිමූර්තිය තුළ හොඳින්ම ක්‍රියා කරයි"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ඔබගේ ඉඩෙන් උපරිම ප්‍රයෝජන ගැනීමට මෙම විකල්පවලින් එකක් උත්සාහ කරන්න"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"සම්පූර්ණ තිරයට යාමට ඔබගේ උපාංගය කරකවන්න"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"එය නැවත ස්ථානගත කිරීමට යෙදුමකට යාබදව දෙවරක් තට්ටු කරන්න"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index 86769b6..8b92ac0 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP ගෙන යන්න"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index daa2021..a231cac 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Niektoré aplikácie fungujú najlepšie v režime na výšku"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Vyskúšajte jednu z týchto možností a využívajte svoj priestor naplno"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zariadenia prejdete do režimu celej obrazovky"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedľa aplikácie zmeníte jej pozíciu"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index 6f6ccb7..390ea39 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
     <string name="pip_move" msgid="1544227837964635439">"Presunúť obraz v obraze"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index b4c7b95..adeaae97 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Nekatere aplikacije najbolje delujejo v navpični postavitvi"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Poskusite eno od teh možnosti za čim boljši izkoristek prostora"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Če želite preklopiti v celozaslonski način, zasukajte napravo."</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvakrat se dotaknite ob aplikaciji, če jo želite prestaviti."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
index 837794a..0cd2f6e4 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
     <string name="pip_move" msgid="1544227837964635439">"Premakni sliko v sliki"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 5051351..2839b4b 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Disa aplikacione funksionojnë më mirë në modalitetin vertikal"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Provo një nga këto opsione për ta shfrytëzuar sa më mirë hapësirën"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rrotullo ekranin për të kaluar në ekran të plotë"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Trokit dy herë pranë një aplikacioni për ta ripozicionuar"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
index 107870d0..da55ddb 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
     <string name="pip_move" msgid="1544227837964635439">"Zhvendos PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 96bb48a..9db6b7c 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Неке апликације најбоље функционишу у усправном режиму"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Испробајте једну од ових опција да бисте на најбољи начин искористили простор"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте уређај за приказ преко целог екрана"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двапут додирните поред апликације да бисте променили њену позицију"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
index ee5690b..a872ced 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
     <string name="pip_move" msgid="1544227837964635439">"Премести слику у слици"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 9fa5c19..f6bd554 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Vissa appar fungerar bäst i stående läge"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Testa med ett av dessa alternativ för att få ut mest möjliga av ytan"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotera skärmen för att gå över till helskärmsläge"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryck snabbt två gånger bredvid en app för att flytta den"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index 7355adf..ae61e87 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
     <string name="pip_move" msgid="1544227837964635439">"Flytta BIB"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 8c026f9..f6e55852 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Baadhi ya programu hufanya kazi vizuri zaidi zikiwa wima"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Jaribu moja kati ya chaguo hizi ili utumie nafasi ya skrini yako kwa ufanisi"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungusha kifaa chako ili uende kwenye hali ya skrini nzima"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Gusa mara mbili karibu na programu ili uihamishe"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index 0ee2841..5bafca1 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
     <string name="pip_move" msgid="1544227837964635439">"Kuhamisha PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index cb3d138..d8334ad 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"சில ஆப்ஸ் \'போர்ட்ரெய்ட்டில்\' சிறப்பாகச் செயல்படும்"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ஸ்பேஸ்களிலிருந்து அதிகப் பலன்களைப் பெற இந்த விருப்பங்களில் ஒன்றைப் பயன்படுத்துங்கள்"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"முழுத்திரைக்குச் செல்ல உங்கள் சாதனத்தைச் சுழற்றவும்"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ஆப்ஸை இடம் மாற்ற, ஆப்ஸுக்கு அடுத்து இருமுறை தட்டவும்"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"சரி"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index 8bcc43b..8a34874 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIPபை நகர்த்து"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 7589e70..7330755 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"కొన్ని యాప్‌లు పోర్ట్రెయిట్‌లో ఉత్తమంగా పని చేస్తాయి"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"మీ ప్రదేశాన్ని ఎక్కువగా ఉపయోగించుకోవడానికి ఈ ఆప్షన్‌లలో ఒకదాన్ని ట్రై చేయండి"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ఫుల్ స్క్రీన్‌కు వెళ్లడానికి మీ పరికరాన్ని తిప్పండి"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"యాప్ స్థానాన్ని మార్చడానికి దాని పక్కన డబుల్-ట్యాప్ చేయండి"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"అర్థమైంది"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 6e80bd7..bfa5df6 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"ఫుల్-స్క్రీన్‌"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIPను తరలించండి"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index d8a33ff..cfee8ea 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"บางแอปทำงานได้ดีที่สุดในแนวตั้ง"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"ลองใช้หนึ่งในตัวเลือกเหล่านี้เพื่อให้ได้ประโยชน์สูงสุดจากพื้นที่ว่าง"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"หมุนอุปกรณ์ให้แสดงเต็มหน้าจอ"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"แตะสองครั้งข้างแอปเพื่อเปลี่ยนตำแหน่ง"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"รับทราบ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
index b6f6369..896664e 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
     <string name="pip_move" msgid="1544227837964635439">"ย้าย PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 35a58b3..eed624d 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"May ilang app na pinakamainam gamitin nang naka-portrait"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Subukan ang isa sa mga opsyong ito para masulit ang iyong space"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"I-rotate ang iyong device para mag-full screen"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Mag-double tap sa tabi ng isang app para iposisyon ito ulit"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index 71ca230..0cc457a 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Isara ang PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
     <string name="pip_move" msgid="1544227837964635439">"Ilipat ang PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 8a9fb75..2b4a2d0 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Bazı uygulamalar dikey modda en iyi performansı gösterir"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Alanınızı en verimli şekilde kullanmak için bu seçeneklerden birini deneyin"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana geçmek için cihazınızı döndürün"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Yeniden konumlandırmak için uygulamanın yanına iki kez dokunun"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index e6ae7f1..da0bf2d 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIP\'yi taşı"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index aac9031..c3411a8 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Деякі додатки найкраще працюють у вертикальній орієнтації"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Щоб максимально ефективно використовувати місце на екрані, спробуйте виконати одну з наведених нижче дій"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Щоб перейти в повноекранний режим, поверніть пристрій"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Щоб перемістити додаток, двічі торкніться області поруч із ним"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
index 97e1f09..4c83bc8 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
     <string name="pip_move" msgid="1544227837964635439">"Перемістити картинку в картинці"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index e3bab32..a31c2be 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"کچھ ایپس پورٹریٹ میں بہترین کام کرتی ہیں"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"اپنی اسپیس کا زیادہ سے زیادہ فائدہ اٹھانے کے لیے ان اختیارات میں سے ایک کو آزمائیں"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"پوری اسکرین پر جانے کیلئے اپنا آلہ گھمائیں"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس کے آگے دو بار تھپتھپائیں"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"سمجھ آ گئی"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index 1418570..c05729a 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -22,4 +22,6 @@
     <string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
     <string name="pip_move" msgid="1544227837964635439">"‏PIP کو منتقل کریں"</string>
+    <string name="pip_expand" msgid="7605396312689038178">"‏PIP کو پھیلائیں"</string>
+    <string name="pip_collapse" msgid="5732233773786896094">"‏PIP کو سکیڑیں"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 54ec89a..2e32225 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Ayrim ilovalar tik holatda ishlashga eng mos"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Muhitdan yanada samarali foydalanish uchun quyidagilardan birini sinang"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Butun ekranda ochish uchun qurilmani buring"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Qayta joylash uchun keyingi ilova ustiga ikki marta bosing"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index 31c762e..bfeaa0d 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
     <string name="pip_move" msgid="1544227837964635439">"PIPni siljitish"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index b683702..8f3cffe 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Một số ứng dụng hoạt động tốt nhất ở chế độ dọc"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Hãy thử một trong các tuỳ chọn sau để tận dụng không gian"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xoay thiết bị để chuyển sang chế độ toàn màn hình"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Nhấn đúp vào bên cạnh ứng dụng để đặt lại vị trí"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index b46cd49..41ff256 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
     <string name="pip_move" msgid="1544227837964635439">"Di chuyển PIP (Ảnh trong ảnh)"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 811d860..19a9d37 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"某些应用在纵向模式下才能发挥最佳效果"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"这些选项都有助于您最大限度地利用屏幕空间,不妨从中择一试试"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋转设备即可进入全屏模式"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在某个应用旁边连续点按两次,即可调整它的位置"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index b6fec63..f68077f 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
     <string name="pip_move" msgid="1544227837964635439">"移动画中画窗口"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 2a01714..0c40e96 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"部分應用程式需要使用直向模式才能發揮最佳效果"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請嘗試以下選項,充分運用螢幕的畫面空間"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕按兩下即可調整位置"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
index b5d54cb..77e848c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
     <string name="pip_move" msgid="1544227837964635439">"移動畫中畫"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 292a439..8691352 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"某些應用程式在直向模式下才能發揮最佳效果"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"請試試這裡的任一方式,以充分運用螢幕畫面的空間"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕觸兩下即可調整位置"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"我知道了"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
index 57db7a8..f085458 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
     <string name="pip_move" msgid="1544227837964635439">"移動子母畫面"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 389eb08..44ffbc6 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -76,13 +76,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
-    <!-- no translation found for letterbox_education_dialog_title (6688664582871779215) -->
-    <skip />
-    <!-- no translation found for letterbox_education_dialog_subtext (4853542518367719562) -->
-    <skip />
-    <!-- no translation found for letterbox_education_screen_rotation_text (5085786687366339027) -->
-    <skip />
-    <!-- no translation found for letterbox_education_reposition_text (1068293354123934727) -->
-    <skip />
+    <string name="letterbox_education_dialog_title" msgid="6688664582871779215">"Amanye ama-app asebenza ngcono uma eme ngobude"</string>
+    <string name="letterbox_education_dialog_subtext" msgid="4853542518367719562">"Zama enye yalezi zinketho ukuze usebenzise isikhala sakho ngokugcwele"</string>
+    <string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungezisa idivayisi yakho ukuze uye esikrinini esigcwele"</string>
+    <string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Thepha kabili eduze kwe-app ukuze uyimise kabusha"</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index 646a488..e238237 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -22,4 +22,8 @@
     <string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
     <string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
     <string name="pip_move" msgid="1544227837964635439">"Hambisa i-PIP"</string>
+    <!-- no translation found for pip_expand (7605396312689038178) -->
+    <skip />
+    <!-- no translation found for pip_collapse (5732233773786896094) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 8d5fdfb..9b41468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -52,9 +52,6 @@
  */
 public class BackAnimationController implements RemoteCallable<BackAnimationController> {
 
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-    public static final boolean IS_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
     private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
             "persist.debug.back_predictability_progress_threshold";
     private static final int PROGRESS_THRESHOLD = SystemProperties
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 6ffcf10..241f1a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -423,7 +423,6 @@
                             WindowContainerTransaction t) {
                         // This is triggered right before the rotation is applied
                         if (fromRotation != toRotation) {
-                            mBubblePositioner.setRotation(toRotation);
                             if (mStackView != null) {
                                 // Layout listener set on stackView will update the positioner
                                 // once the rotation is applied
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 127d5a8..75b19fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
 import android.graphics.PointF;
@@ -112,10 +113,6 @@
         update();
     }
 
-    public void setRotation(int rotation) {
-        mRotation = rotation;
-    }
-
     /**
      * Available space and inset information. Call this when config changes
      * occur or when added to a window.
@@ -273,7 +270,8 @@
 
     /** @return whether the device is in landscape orientation. */
     public boolean isLandscape() {
-        return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+        return mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
     }
 
     /** @return whether the screen is considered large. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 9219baa..c6a68dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1237,7 +1237,7 @@
                 b.getExpandedView().updateFontSize();
             }
         }
-        if (mBubbleOverflow != null) {
+        if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
             mBubbleOverflow.getExpandedView().updateFontSize();
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 656dae3..ee4d5ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -40,6 +40,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
 import java.lang.ref.WeakReference;
@@ -105,9 +106,8 @@
 
     private CompatUICallback mCallback;
 
-    // Only show once automatically in the process life.
-    private boolean mHasShownSizeCompatHint;
-    private boolean mHasShownCameraCompatHint;
+    // Only show each hint once automatically in the process life.
+    private final CompatUIHintsState mCompatUIHintsState;
 
     // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
     // be shown.
@@ -127,6 +127,7 @@
         mMainExecutor = mainExecutor;
         mDisplayController.addDisplayWindowListener(this);
         mImeController.addPositionProcessor(this);
+        mCompatUIHintsState = new CompatUIHintsState();
     }
 
     /** Returns implementation of {@link CompatUI}. */
@@ -259,19 +260,9 @@
     @VisibleForTesting
     CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
             ShellTaskOrganizer.TaskListener taskListener) {
-        final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
+        return new CompatUIWindowManager(context,
                 taskInfo, mSyncQueue, mCallback, taskListener,
-                mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
-                mHasShownCameraCompatHint);
-        // TODO(b/218304113): updates values only if hints are actually shown to the user.
-        // Only show hints for the first time.
-        if (taskInfo.topActivityInSizeCompat) {
-            mHasShownSizeCompatHint = true;
-        }
-        if (taskInfo.hasCameraCompatControl()) {
-            mHasShownCameraCompatHint = true;
-        }
-        return compatUIWindowManager;
+                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
     }
 
     private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
@@ -292,9 +283,8 @@
         if (context == null) {
             return;
         }
-        LetterboxEduWindowManager newLayout = new LetterboxEduWindowManager(context, taskInfo,
-                mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                this::onLetterboxEduDismissed);
+        LetterboxEduWindowManager newLayout = createLetterboxEduWindowManager(context, taskInfo,
+                taskListener);
         if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) {
             // The new layout is eligible to be shown, make it the active layout.
             if (mActiveLetterboxEduLayout != null) {
@@ -307,6 +297,14 @@
         }
     }
 
+    @VisibleForTesting
+    LetterboxEduWindowManager createLetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        return new LetterboxEduWindowManager(context, taskInfo,
+                mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
+                this::onLetterboxEduDismissed);
+    }
+
     private void onLetterboxEduDismissed() {
         mActiveLetterboxEduLayout = null;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 7c6780a..bce3ec4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -59,9 +59,7 @@
     int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
 
     @VisibleForTesting
-    boolean mShouldShowSizeCompatHint;
-    @VisibleForTesting
-    boolean mShouldShowCameraCompatHint;
+    CompatUIHintsState mCompatUIHintsState;
 
     @Nullable
     @VisibleForTesting
@@ -70,13 +68,12 @@
     CompatUIWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, CompatUICallback callback,
             ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
-            boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+            CompatUIHintsState compatUIHintsState) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.topActivityInSizeCompat;
         mCameraCompatControlState = taskInfo.cameraCompatControlState;
-        mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
-        mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
+        mCompatUIHintsState = compatUIHintsState;
     }
 
     @Override
@@ -212,18 +209,18 @@
         }
         // Size Compat mode restart button.
         mLayout.setRestartButtonVisibility(mHasSizeCompat);
-        if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+        // Only show by default for the first time.
+        if (mHasSizeCompat && !mCompatUIHintsState.mHasShownSizeCompatHint) {
             mLayout.setSizeCompatHintVisibility(/* show= */ true);
-            // Only show by default for the first time.
-            mShouldShowSizeCompatHint = false;
+            mCompatUIHintsState.mHasShownSizeCompatHint = true;
         }
 
         // Camera control for stretched issues.
         mLayout.setCameraControlVisibility(shouldShowCameraControl());
-        if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+        // Only show by default for the first time.
+        if (shouldShowCameraControl() && !mCompatUIHintsState.mHasShownCameraCompatHint) {
             mLayout.setCameraCompatHintVisibility(/* show= */ true);
-            // Only show by default for the first time.
-            mShouldShowCameraCompatHint = false;
+            mCompatUIHintsState.mHasShownCameraCompatHint = true;
         }
         if (shouldShowCameraControl()) {
             mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
@@ -234,4 +231,15 @@
         return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
                 && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
     }
+
+    /**
+     * A class holding the state of the compat UI hints, which is shared between all compat UI
+     * window managers.
+     */
+    static class CompatUIHintsState {
+        @VisibleForTesting
+        boolean mHasShownSizeCompatHint;
+        @VisibleForTesting
+        boolean mHasShownCameraCompatHint;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 5679bc4..face243 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -22,6 +22,10 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
+
 import android.annotation.Nullable;
 import android.app.TaskInfo;
 import android.content.Context;
@@ -110,7 +114,8 @@
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      * @return whether the layout is eligible to be shown.
      */
-    protected boolean createLayout(boolean canShow) {
+    @VisibleForTesting(visibility = PROTECTED)
+    public boolean createLayout(boolean canShow) {
         if (!eligibleToShowLayout()) {
             return false;
         }
@@ -184,7 +189,8 @@
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      * @return whether the layout is eligible to be shown.
      */
-    protected boolean updateCompatInfo(TaskInfo taskInfo,
+    @VisibleForTesting(visibility = PROTECTED)
+    public boolean updateCompatInfo(TaskInfo taskInfo,
             ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
         final Configuration prevTaskConfig = mTaskConfig;
         final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
@@ -201,7 +207,8 @@
 
         View layout = getLayout();
         if (layout == null || prevTaskListener != taskListener) {
-            // TaskListener changed, recreate the layout for new surface parent.
+            // Layout wasn't created yet or TaskListener changed, recreate the layout for new
+            // surface parent.
             release();
             return createLayout(canShow);
         }
@@ -227,7 +234,8 @@
      *
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      */
-    void updateVisibility(boolean canShow) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void updateVisibility(boolean canShow) {
         View layout = getLayout();
         if (layout == null) {
             // Layout may not have been created because it was hidden previously.
@@ -242,7 +250,8 @@
     }
 
     /** Called when display layout changed. */
-    void updateDisplayLayout(DisplayLayout displayLayout) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void updateDisplayLayout(DisplayLayout displayLayout) {
         final Rect prevStableBounds = mStableBounds;
         final Rect curStableBounds = new Rect();
         displayLayout.getStableBounds(curStableBounds);
@@ -255,7 +264,7 @@
     }
 
     /** Called when the surface is ready to be placed under the task surface. */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = PRIVATE)
     void attachToParentSurface(SurfaceControl.Builder b) {
         mTaskListener.attachChildSurfaceToTask(mTaskId, b);
     }
@@ -347,8 +356,9 @@
         return result;
     }
 
-    @VisibleForTesting
-    SurfaceControlViewHost createSurfaceViewHost() {
+    /** Creates a {@link SurfaceControlViewHost} for this window manager. */
+    @VisibleForTesting(visibility = PRIVATE)
+    public SurfaceControlViewHost createSurfaceViewHost() {
         return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
index 3810eca..03986ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
@@ -39,7 +39,6 @@
 /**
  * Controls the enter/exit animations of the letterbox education.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduAnimationController {
     private static final String TAG = "LetterboxEduAnimation";
 
@@ -99,15 +98,10 @@
     /**
      * Starts both the background dim fade-out animation and the dialog exit animation.
      */
-    void startExitAnimation(@Nullable LetterboxEduDialogLayout layout, Runnable endCallback) {
+    void startExitAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
         // Cancel any previous animation if it's still running.
         cancelAnimation();
 
-        if (layout == null) {
-            endCallback.run();
-            return;
-        }
-
         final View dialogContainer = layout.getDialogContainer();
         mDialogAnimation = loadAnimation(WindowAnimation_windowExitAnimation);
         if (mDialogAnimation == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
index fc6fd3f..02197f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
@@ -29,18 +29,28 @@
 /**
  * Custom layout for Letterbox Education dialog action.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduDialogActionLayout extends FrameLayout {
 
-    LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    public LetterboxEduDialogActionLayout(Context context) {
+        this(context, null);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
 
         TypedArray styledAttributes =
                 context.getTheme().obtainStyledAttributes(
-                        attrs,
-                        R.styleable.LetterboxEduDialogActionLayout,
-                        /* defStyleAttr= */ 0,
-                        /* defStyleRes= */ 0);
+                        attrs, R.styleable.LetterboxEduDialogActionLayout, defStyleAttr,
+                        defStyleRes);
         int iconId = styledAttributes.getResourceId(
                 R.styleable.LetterboxEduDialogActionLayout_icon, 0);
         String text = styledAttributes.getString(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index bb6fe98..2da6a6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.compatui.letterboxedu;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
@@ -31,7 +32,6 @@
  * <p>This layout should fill the entire task and the background around the dialog acts as the
  * background dim which dismisses the dialog when clicked.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduDialogLayout extends ConstraintLayout {
 
     // The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
@@ -68,16 +68,16 @@
     /**
      * Register a callback for the dismiss button and background dim.
      *
-     * @param callback The callback to register
+     * @param callback The callback to register or null if all on click listeners should be removed.
      */
-    void setDismissOnClickListener(Runnable callback) {
-        findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(
-                view -> callback.run());
+    void setDismissOnClickListener(@Nullable Runnable callback) {
+        final OnClickListener listener = callback == null ? null : view -> callback.run();
+        findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(listener);
         // Clicks on the background dim should also dismiss the dialog.
-        setOnClickListener(view -> callback.run());
+        setOnClickListener(listener);
         // We add a no-op on-click listener to the dialog container so that clicks on it won't
         // propagate to the listener of the layout (which represents the background dim).
-        mDialogContainer.setOnClickListener(view -> {});
+        mDialogContainer.setOnClickListener(callback == null ? null : view -> {});
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index bb4d427..30b9f08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -29,6 +29,7 @@
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayLayout;
@@ -38,7 +39,6 @@
 /**
  * Window manager for the Letterbox Education.
  */
-// TODO(b/215316431): Add tests
 public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
 
     /**
@@ -51,7 +51,8 @@
      * The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
      * Education for specific packages and which user has seen the full dialog for any package.
      */
-    private static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
+    @VisibleForTesting
+    static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
             "has_seen_letterbox_education";
 
     /**
@@ -65,19 +66,37 @@
     private boolean mEligibleForLetterboxEducation;
 
     @Nullable
-    private LetterboxEduDialogLayout mLayout;
+    @VisibleForTesting
+    LetterboxEduDialogLayout mLayout;
 
     private final Runnable mOnDismissCallback;
 
+    /**
+     * The vertical margin between the dialog container and the task stable bounds (excluding
+     * insets).
+     */
+    private final int mDialogVerticalMargin;
+
     public LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
             DisplayLayout displayLayout, Runnable onDismissCallback) {
+        this(context, taskInfo, syncQueue, taskListener, displayLayout, onDismissCallback,
+                new LetterboxEduAnimationController(context));
+    }
+
+    @VisibleForTesting
+    LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Runnable onDismissCallback,
+            LetterboxEduAnimationController animationController) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mOnDismissCallback = onDismissCallback;
+        mAnimationController = animationController;
         mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
-        mAnimationController = new LetterboxEduAnimationController(context);
         mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
                 Context.MODE_PRIVATE);
+        mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_education_dialog_margin);
     }
 
     @Override
@@ -124,13 +143,12 @@
         }
         final View dialogContainer = mLayout.getDialogContainer();
         MarginLayoutParams marginParams = (MarginLayoutParams) dialogContainer.getLayoutParams();
-        int verticalMargin = (int) mContext.getResources().getDimension(
-                R.dimen.letterbox_education_dialog_margin);
 
         final Rect taskBounds = getTaskBounds();
         final Rect taskStableBounds = getTaskStableBounds();
-        marginParams.topMargin = taskStableBounds.top - taskBounds.top + verticalMargin;
-        marginParams.bottomMargin = taskBounds.bottom - taskStableBounds.bottom + verticalMargin;
+        marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+        marginParams.bottomMargin =
+                taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
         dialogContainer.setLayoutParams(marginParams);
     }
 
@@ -147,6 +165,10 @@
     }
 
     private void onDismiss() {
+        if (mLayout == null) {
+            return;
+        }
+        mLayout.setDismissOnClickListener(null);
         mAnimationController.startExitAnimation(mLayout, () -> {
             release();
             mOnDismissCallback.run();
@@ -204,7 +226,8 @@
         return String.valueOf(mContext.getUserId());
     }
 
-    private boolean isTaskbarEduShowing() {
+    @VisibleForTesting
+    boolean isTaskbarEduShowing() {
         return Settings.Secure.getInt(mContext.getContentResolver(),
                 LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 2e54c79..c94f3d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -168,8 +168,8 @@
 
     @WMSingleton
     @Provides
-    static DragAndDrop provideDragAndDrop(DragAndDropController dragAndDropController) {
-        return dragAndDropController.asDragAndDrop();
+    static Optional<DragAndDrop> provideDragAndDrop(DragAndDropController dragAndDropController) {
+        return Optional.of(dragAndDropController.asDragAndDrop());
     }
 
     @WMSingleton
@@ -184,8 +184,8 @@
 
     @WMSingleton
     @Provides
-    static CompatUI provideCompatUI(CompatUIController compatUIController) {
-        return compatUIController.asCompatUI();
+    static Optional<CompatUI> provideCompatUI(CompatUIController compatUIController) {
+        return Optional.of(compatUIController.asCompatUI());
     }
 
     @WMSingleton
@@ -699,10 +699,7 @@
             Context context,
             @ShellMainThread ShellExecutor shellExecutor
     ) {
-        if (BackAnimationController.IS_ENABLED) {
-            return Optional.of(
-                    new BackAnimationController(shellExecutor, context));
-        }
-        return Optional.empty();
+        return Optional.of(
+                new BackAnimationController(shellExecutor, context));
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index 5c205f9..8f9636c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -27,7 +27,10 @@
 import android.os.Looper;
 import android.os.Trace;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
@@ -35,7 +38,6 @@
 import com.android.wm.shell.common.annotations.ShellAnimationThread;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
-import com.android.wm.shell.R;
 
 import dagger.Module;
 import dagger.Provides;
@@ -53,7 +55,7 @@
     /**
      * Returns whether to enable a separate shell thread for the shell features.
      */
-    private static boolean enableShellMainThread(Context context) {
+    public static boolean enableShellMainThread(Context context) {
         return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
     }
 
@@ -85,23 +87,41 @@
     }
 
     /**
+     * Creates a shell main thread to be injected into the shell components.  This does not provide
+     * the {@param HandleThread}, but is used to create the thread prior to initializing the
+     * WM component, and is explicitly bound.
+     *
+     * See {@link com.android.systemui.SystemUIFactory#init(Context, boolean)}.
+     */
+    public static HandlerThread createShellMainThread() {
+        HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
+        return mainThread;
+    }
+
+    /**
      * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe
      * multiple types of messages, etc.)
+     *
+     * @param mainThread If non-null, this thread is expected to be started already
      */
     @WMSingleton
     @Provides
     @ShellMainThread
     public static Handler provideShellMainHandler(Context context,
+            @Nullable @ShellMainThread HandlerThread mainThread,
             @ExternalMainThread Handler sysuiMainHandler) {
         if (enableShellMainThread(context)) {
-             HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
-             mainThread.start();
-             if (Build.IS_DEBUGGABLE) {
-                 mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
-                 mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
-                         MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
-             }
-             return Handler.createAsync(mainThread.getLooper());
+            if (mainThread == null) {
+                // If this thread wasn't pre-emptively started, then create and start it
+                mainThread = createShellMainThread();
+                mainThread.start();
+            }
+            if (Build.IS_DEBUGGABLE) {
+                mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+                mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
+                        MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
+            }
+            return Handler.createAsync(mainThread.getLooper());
         }
         return sysuiMainHandler;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 101295d..11ecc91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -35,6 +35,9 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -54,6 +57,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -205,6 +209,7 @@
                 break;
             case ACTION_DRAG_ENTERED:
                 pd.dragLayout.show();
+                pd.dragLayout.update(event);
                 break;
             case ACTION_DRAG_LOCATION:
                 pd.dragLayout.update(event);
@@ -250,10 +255,6 @@
                 // Hide the window if another drag hasn't been started while animating the drop
                 setDropTargetWindowVisibility(pd, View.INVISIBLE);
             }
-
-            // Clean up the drag surface
-            mTransaction.reparent(dragSurface, null);
-            mTransaction.apply();
         });
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index e8bae0f..7568310 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -123,6 +123,13 @@
     }
 
     /**
+     * Returns the number of targets.
+     */
+    int getNumTargets() {
+        return mTargets.size();
+    }
+
+    /**
      * Returns the target's regions based on the current state of the device and display.
      */
     @NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index d395f95..25fe8b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.draganddrop;
 
 import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -24,6 +25,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.StatusBarManager;
@@ -44,6 +46,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -135,6 +138,12 @@
         }
     }
 
+    private void updateContainerMarginsForSingleTask() {
+        mDropZoneView1.setContainerMargin(
+                mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
+        mDropZoneView2.setContainerMargin(0, 0, 0, 0);
+    }
+
     private void updateContainerMargins(int orientation) {
         final float halfMargin = mDisplayMargin / 2f;
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
@@ -165,11 +174,20 @@
         if (!alreadyInSplit) {
             ActivityManager.RunningTaskInfo taskInfo1 = mPolicy.getLatestRunningTask();
             if (taskInfo1 != null) {
-                Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
-                int bgColor1 = getResizingBackgroundColor(taskInfo1);
-                mDropZoneView1.setAppInfo(bgColor1, icon1);
-                mDropZoneView2.setAppInfo(bgColor1, icon1);
-                updateDropZoneSizes(null, null); // passing null splits the views evenly
+                final int activityType = taskInfo1.getActivityType();
+                if (activityType == ACTIVITY_TYPE_STANDARD) {
+                    Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
+                    int bgColor1 = getResizingBackgroundColor(taskInfo1);
+                    mDropZoneView1.setAppInfo(bgColor1, icon1);
+                    mDropZoneView2.setAppInfo(bgColor1, icon1);
+                    updateDropZoneSizes(null, null); // passing null splits the views evenly
+                } else {
+                    // We use the first drop zone to show the fullscreen highlight, and don't need
+                    // to set additional info
+                    mDropZoneView1.setForceIgnoreBottomMargin(true);
+                    updateDropZoneSizesForSingleTask();
+                    updateContainerMarginsForSingleTask();
+                }
             }
         } else {
             // We're already in split so get taskInfo from the controller to populate icon / color.
@@ -195,6 +213,21 @@
         }
     }
 
+    private void updateDropZoneSizesForSingleTask() {
+        final LinearLayout.LayoutParams dropZoneView1 =
+                (LayoutParams) mDropZoneView1.getLayoutParams();
+        final LinearLayout.LayoutParams dropZoneView2 =
+                (LayoutParams) mDropZoneView2.getLayoutParams();
+        dropZoneView1.width = MATCH_PARENT;
+        dropZoneView1.height = MATCH_PARENT;
+        dropZoneView2.width = 0;
+        dropZoneView2.height = 0;
+        dropZoneView1.weight = 1;
+        dropZoneView2.weight = 0;
+        mDropZoneView1.setLayoutParams(dropZoneView1);
+        mDropZoneView2.setLayoutParams(dropZoneView2);
+    }
+
     /**
      * Sets the size of the two drop zones based on the provided bounds. The divider sits between
      * the views and its size is included in the calculations.
@@ -265,9 +298,12 @@
                 // Animating to no target
                 animateSplitContainers(false, null /* animCompleteCallback */);
             } else if (mCurrentTarget == null) {
-                // Animating to first target
-                animateSplitContainers(true, null /* animCompleteCallback */);
-                animateHighlight(target);
+                if (mPolicy.getNumTargets() == 1) {
+                    animateFullscreenContainer(true);
+                } else {
+                    animateSplitContainers(true, null /* animCompleteCallback */);
+                    animateHighlight(target);
+                }
             } else {
                 // Switching between targets
                 mDropZoneView1.animateSwitch();
@@ -283,6 +319,10 @@
     public void hide(DragEvent event, Runnable hideCompleteCallback) {
         mIsShowing = false;
         animateSplitContainers(false, hideCompleteCallback);
+        // Reset the state if we previously force-ignore the bottom margin
+        mDropZoneView1.setForceIgnoreBottomMargin(false);
+        mDropZoneView2.setForceIgnoreBottomMargin(false);
+        updateContainerMargins(getResources().getConfiguration().orientation);
         mCurrentTarget = null;
     }
 
@@ -297,11 +337,63 @@
         // Process the drop
         mPolicy.handleDrop(mCurrentTarget, event.getClipData());
 
-        // TODO(b/169894807): Coordinate with dragSurface
+        // Start animating the drop UI out with the drag surface
         hide(event, dropCompleteCallback);
+        hideDragSurface(dragSurface);
         return handledDrop;
     }
 
+    private void hideDragSurface(SurfaceControl dragSurface) {
+        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        final ValueAnimator dragSurfaceAnimator = ValueAnimator.ofFloat(0f, 1f);
+        // Currently the splash icon animation runs with the default ValueAnimator duration of
+        // 300ms
+        dragSurfaceAnimator.setDuration(300);
+        dragSurfaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        dragSurfaceAnimator.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            float alpha = 1f - t;
+            // TODO: Scale the drag surface as well once we make all the source surfaces
+            //       consistent
+            tx.setAlpha(dragSurface, alpha);
+            tx.apply();
+        });
+        dragSurfaceAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCanceled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                cleanUpSurface();
+                mCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCanceled) {
+                    // Already handled above
+                    return;
+                }
+                cleanUpSurface();
+            }
+
+            private void cleanUpSurface() {
+                // Clean up the drag surface
+                tx.remove(dragSurface);
+                tx.apply();
+            }
+        });
+        dragSurfaceAnimator.start();
+    }
+
+    private void animateFullscreenContainer(boolean visible) {
+        mStatusBarManager.disable(visible
+                ? HIDE_STATUS_BAR_FLAGS
+                : DISABLE_NONE);
+        // We're only using the first drop zone if there is one fullscreen target
+        mDropZoneView1.setShowingMargin(visible);
+        mDropZoneView1.setShowingHighlight(visible);
+    }
+
     private void animateSplitContainers(boolean visible, Runnable animCompleteCallback) {
         mStatusBarManager.disable(visible
                 ? HIDE_STATUS_BAR_FLAGS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index a3ee8ae..38870bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -65,6 +65,7 @@
     private final float[] mContainerMargin = new float[4];
     private float mCornerRadius;
     private float mBottomInset;
+    private boolean mIgnoreBottomMargin;
     private int mMarginColor; // i.e. color used for negative space like the container insets
 
     private boolean mShowingHighlight;
@@ -141,6 +142,14 @@
         }
     }
 
+    /** Ignores the bottom margin provided by the insets. */
+    public void setForceIgnoreBottomMargin(boolean ignoreBottomMargin) {
+        mIgnoreBottomMargin = ignoreBottomMargin;
+        if (mMarginPercent > 0) {
+            mMarginView.invalidate();
+        }
+    }
+
     /** Sets the bottom inset so the drop zones are above bottom navigation. */
     public void setBottomInset(float bottom) {
         mBottomInset = bottom;
@@ -257,7 +266,8 @@
             mPath.addRoundRect(mContainerMargin[0] * mMarginPercent,
                     mContainerMargin[1] * mMarginPercent,
                     getWidth() - (mContainerMargin[2] * mMarginPercent),
-                    getHeight() - (mContainerMargin[3] * mMarginPercent) - mBottomInset,
+                    getHeight() - (mContainerMargin[3] * mMarginPercent)
+                            - (mIgnoreBottomMargin ? 0 : mBottomInset),
                     mCornerRadius * mMarginPercent,
                     mCornerRadius * mMarginPercent,
                     Path.Direction.CW);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index e616172..77fd228 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -620,7 +620,7 @@
                     setCurrentValue(bounds);
                     final Rect insets = computeInsets(fraction);
                     final float degree, x, y;
-                    if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+                    if (Transitions.SHELL_TRANSITIONS_ROTATION) {
                         if (rotationDelta == ROTATION_90) {
                             degree = 90 * (1 - fraction);
                             x = fraction * (end.left - start.left)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 67b3983..1eb9501 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -310,6 +310,10 @@
         return mPipTransitionState.isInPip();
     }
 
+    private boolean isLaunchIntoPipTask() {
+        return mPictureInPictureParams != null && mPictureInPictureParams.isLaunchIntoPip();
+    }
+
     /**
      * Returns whether the entry animation is waiting to be started.
      */
@@ -397,6 +401,10 @@
         }
 
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (isLaunchIntoPipTask()) {
+            exitLaunchIntoPipTask(wct);
+            return;
+        }
 
         if (ENABLE_SHELL_TRANSITIONS) {
             if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
@@ -468,6 +476,14 @@
         });
     }
 
+    private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
+        wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
+        mTaskOrganizer.applyTransaction(wct);
+
+        // Remove the PiP with fade-out animation right after the host Task is brought to front.
+        removePip();
+    }
+
     private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
         // Reset the final windowing mode.
         wct.setWindowingMode(mToken, getOutPipWindowingMode());
@@ -563,6 +579,13 @@
             Log.d(TAG, "Alpha animation is expired. Use bounds animation.");
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
         }
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            // For Shell transition, we will animate the window in PipTransition#startAnimation
+            // instead of #onTaskAppeared.
+            return;
+        }
+
         if (mWaitForFixedRotation) {
             onTaskAppearedWithFixedRotation();
             return;
@@ -572,15 +595,6 @@
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
 
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-                mPipMenuController.attach(mLeash);
-            } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
-                mOneShotAnimationType = ANIM_TYPE_BOUNDS;
-            }
-            return;
-        }
-
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             mPipMenuController.attach(mLeash);
             final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
@@ -729,7 +743,7 @@
     }
 
     /**
-     * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
+     * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int, boolean)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
      *   - as a result of removeRootTasksInWindowingModes from WM
      *   - activity itself is died
@@ -813,6 +827,16 @@
         mNextRotation = newRotation;
         mWaitForFixedRotation = true;
 
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            // The fixed rotation will also be included in the transition info. However, if it is
+            // not a PIP transition (such as open another app to different orientation),
+            // PIP transition handler may not be aware of the fixed rotation start.
+            // Notify the PIP transition handler so that it can fade out the PIP window early for
+            // fixed transition of other windows.
+            mPipTransitionController.onFixedRotationStarted();
+            return;
+        }
+
         if (mPipTransitionState.isInPip()) {
             // Fade out the existing PiP to avoid jump cut during seamless rotation.
             fadeExistingPip(false /* show */);
@@ -824,6 +848,10 @@
         if (!mWaitForFixedRotation) {
             return;
         }
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            clearWaitForFixedRotation();
+            return;
+        }
         if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
             if (mPipTransitionState.getInSwipePipToHomeTransition()) {
                 onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 3e5d5f6..60aac68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.util.RotationUtils.deltaRotation;
@@ -32,9 +33,11 @@
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
@@ -47,6 +50,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -86,6 +90,16 @@
     /** The Task window that is currently in PIP windowing mode. */
     @Nullable
     private WindowContainerToken mCurrentPipTaskToken;
+    /** Whether display is in fixed rotation. */
+    private boolean mInFixedRotation;
+    /**
+     * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+     * meaningful if {@link #mInFixedRotation} is true.
+     */
+    @Surface.Rotation
+    private int mFixedRotation;
+    /** Whether the PIP window has fade out for fixed rotation. */
+    private boolean mHasFadeOut;
 
     public PipTransition(Context context,
             PipBoundsState pipBoundsState,
@@ -136,35 +150,41 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+        final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+        mInFixedRotation = fixedRotationChange != null;
+        mFixedRotation = mInFixedRotation
+                ? fixedRotationChange.getEndFixedRotation()
+                : ROTATION_UNDEFINED;
+
         // Exiting PIP.
         final int type = info.getType();
         if (transition.equals(mExitTransition)) {
             mExitDestinationBounds.setEmpty();
             mExitTransition = null;
-
+            mHasFadeOut = false;
             if (mFinishCallback != null) {
                 mFinishCallback.onTransitionFinished(null, null);
                 mFinishCallback = null;
                 throw new RuntimeException("Previous callback not called, aborting exit PIP.");
             }
 
-            final TransitionInfo.Change exitPipChange = findCurrentPipChange(info);
-            if (exitPipChange == null) {
+            if (currentPipChange == null) {
                 throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
             }
 
             switch (type) {
                 case TRANSIT_EXIT_PIP:
                     startExitAnimation(info, startTransaction, finishTransaction, finishCallback,
-                            exitPipChange);
+                            currentPipChange);
                     break;
                 case TRANSIT_EXIT_PIP_TO_SPLIT:
                     startExitToSplitAnimation(info, startTransaction, finishTransaction,
-                            finishCallback, exitPipChange);
+                            finishCallback, currentPipChange);
                     break;
                 case TRANSIT_REMOVE_PIP:
                     removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
-                            exitPipChange);
+                            currentPipChange);
                     break;
                 default:
                     throw new IllegalStateException("mExitTransition with unexpected transit type="
@@ -177,7 +197,6 @@
         // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
         // happen when a new activity requests enter PIP). In this case, we just show this Task in
         // its end state, and play other animation as normal.
-        final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
         if (currentPipChange != null
                 && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
             resetPrevPip(currentPipChange, startTransaction);
@@ -193,6 +212,12 @@
         if (currentPipChange != null) {
             updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
         }
+
+        // Fade in the fadeout PIP when the fixed rotation is finished.
+        if (mPipTransitionState.isInPip() && !mInFixedRotation && mHasFadeOut) {
+            fadeExistingPip(true /* show */);
+        }
+
         return false;
     }
 
@@ -242,9 +267,8 @@
     public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
             @PipAnimationController.TransitionDirection int direction,
             @Nullable SurfaceControl.Transaction tx) {
-
         if (isInPipDirection(direction)) {
-            mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+            mPipTransitionState.setTransitionState(ENTERED_PIP);
         }
         // If there is an expected exit transition, then the exit will be "merged" into this
         // transition so don't fire the finish-callback in that case.
@@ -268,6 +292,16 @@
         mFinishCallback = null;
     }
 
+    @Override
+    public void onFixedRotationStarted() {
+        // The transition with this fixed rotation may be handled by other handler before reaching
+        // PipTransition, so we cannot do this in #startAnimation.
+        if (mPipTransitionState.getTransitionState() == ENTERED_PIP && !mHasFadeOut) {
+            // Fade out the existing PiP to avoid jump cut during seamless rotation.
+            fadeExistingPip(false /* show */);
+        }
+    }
+
     @Nullable
     private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
         if (mCurrentPipTaskToken == null) {
@@ -282,6 +316,17 @@
         return null;
     }
 
+    @Nullable
+    private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
+                return change;
+            }
+        }
+        return null;
+    }
+
     private void startExitAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -453,6 +498,7 @@
         }
         // Keep track of the PIP task.
         mCurrentPipTaskToken = enterPip.getContainer();
+        mHasFadeOut = false;
 
         if (mFinishCallback != null) {
             mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
@@ -465,12 +511,25 @@
             startTransaction.show(wallpaper.getLeash());
             startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
         }
+        // Make sure other open changes are visible as entering PIP. Some may be hidden in
+        // Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change == enterPip || change == wallpaper) {
+                continue;
+            }
+            if (isOpeningType(change.getMode())) {
+                final SurfaceControl leash = change.getLeash();
+                startTransaction.show(leash).setAlpha(leash, 1.f);
+            }
+        }
 
         mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         mFinishCallback = finishCallback;
+        final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation();
         return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
                 startTransaction, finishTransaction, enterPip.getStartRotation(),
-                enterPip.getEndRotation());
+                endRotation);
     }
 
     private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
@@ -481,25 +540,36 @@
                 taskInfo.topActivityInfo);
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+        int rotationDelta = deltaRotation(startRotation, endRotation);
+        Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                taskInfo.pictureInPictureParams, currentBounds);
+        if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+            // Need to get the bounds of new rotation in old rotation for fixed rotation,
+            sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation,
+                    taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect);
+        }
         PipAnimationController.PipTransitionAnimator animator;
         // Set corner radius for entering pip.
         mSurfaceTransactionHelper
                 .crop(finishTransaction, leash, destinationBounds)
                 .round(finishTransaction, leash, true /* applyCornerRadius */);
+        mPipMenuController.attach(leash);
+
         if (taskInfo.pictureInPictureParams != null
                 && taskInfo.pictureInPictureParams.isAutoEnterEnabled()
                 && mPipTransitionState.getInSwipePipToHomeTransition()) {
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
-
-            // PiP menu is attached late in the process here to avoid any artifacts on the leash
-            // caused by addShellRoot when in gesture navigation mode.
-            mPipMenuController.attach(leash);
             SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
             tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
                     .setPosition(leash, destinationBounds.left, destinationBounds.top)
                     .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
             startTransaction.merge(tx);
             startTransaction.apply();
+            if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+                // For fixed rotation, set the destination bounds to the new rotation coordinates
+                // at the end.
+                destinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+            }
             mPipBoundsState.setBounds(destinationBounds);
             onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, null /* tx */);
             sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
@@ -507,17 +577,14 @@
             return true;
         }
 
-        int rotationDelta = deltaRotation(endRotation, startRotation);
         if (rotationDelta != Surface.ROTATION_0) {
             Matrix tmpTransform = new Matrix();
-            tmpTransform.postRotate(rotationDelta == Surface.ROTATION_90
-                    ? Surface.ROTATION_270 : Surface.ROTATION_90);
+            tmpTransform.postRotate(rotationDelta);
             startTransaction.setMatrix(leash, tmpTransform, new float[9]);
         }
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            final Rect sourceHintRect =
-                    PipBoundsAlgorithm.getValidSourceHintRect(
-                            taskInfo.pictureInPictureParams, currentBounds);
+            // Reverse the rotation for Shell transition animation.
+            rotationDelta = deltaRotation(rotationDelta, 0);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
                     currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
                     0 /* startingAngle */, rotationDelta);
@@ -528,9 +595,6 @@
             }
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             startTransaction.setAlpha(leash, 0f);
-            // PiP menu is attached late in the process here to avoid any artifacts on the leash
-            // caused by addShellRoot when in gesture navigation mode.
-            mPipMenuController.attach(leash);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
                     0f, 1f);
             mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -541,12 +605,47 @@
         startTransaction.apply();
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
-                .setDuration(mEnterExitAnimationDuration)
-                .start();
+                .setDuration(mEnterExitAnimationDuration);
+        if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+            // For fixed rotation, the animation destination bounds is in old rotation coordinates.
+            // Set the destination bounds to new coordinates after the animation is finished.
+            // ComputeRotatedBounds has changed the DisplayLayout without affecting the animation.
+            animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
+        }
+        animator.start();
 
         return true;
     }
 
+    /** Computes destination bounds in old rotation and returns source hint rect if available. */
+    @Nullable
+    private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation,
+            TaskInfo taskInfo, int direction, Rect outDestinationBounds,
+            @Nullable Rect sourceHintRect) {
+        if (direction == TRANSITION_DIRECTION_TO_PIP) {
+            mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+            final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+            // Transform the destination bounds to current display coordinates.
+            rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
+            // When entering PiP (from button navigation mode), adjust the source rect hint by
+            // display cutout if applicable.
+            if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) {
+                if (rotationDelta == Surface.ROTATION_270) {
+                    sourceHintRect.offset(taskInfo.displayCutoutInsets.left,
+                            taskInfo.displayCutoutInsets.top);
+                }
+            }
+        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
+            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
+                    rotationDelta);
+            return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams,
+                    rotatedDestinationBounds);
+        }
+        return sourceHintRect;
+    }
+
     private void startExitToSplitAnimation(TransitionInfo info,
             SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction,
@@ -595,6 +694,13 @@
         startTransaction.setCornerRadius(leash, 0);
         startTransaction.setPosition(leash, bounds.left, bounds.top);
 
+        if (mHasFadeOut && prevPipChange.getTaskInfo().isVisible()) {
+            if (mPipAnimationController.getCurrentAnimator() != null) {
+                mPipAnimationController.getCurrentAnimator().cancel();
+            }
+            startTransaction.setAlpha(leash, 1);
+        }
+        mHasFadeOut = false;
         mCurrentPipTaskToken = null;
         mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
     }
@@ -615,6 +721,25 @@
                 .round(finishTransaction, leash, isInPip);
     }
 
+    /** Hides and shows the existing PIP during fixed rotation transition of other activities. */
+    private void fadeExistingPip(boolean show) {
+        final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
+        final TaskInfo taskInfo = mPipOrganizer.getTaskInfo();
+        if (leash == null || !leash.isValid() || taskInfo == null) {
+            Log.w(TAG, "Invalid leash on fadeExistingPip: " + leash);
+            return;
+        }
+        final float alphaStart = show ? 0 : 1;
+        final float alphaEnd = show ? 1 : 0;
+        mPipAnimationController
+                .getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd)
+                .setTransitionDirection(TRANSITION_DIRECTION_SAME)
+                .setPipAnimationCallback(mPipAnimationCallback)
+                .setDuration(mEnterExitAnimationDuration)
+                .start();
+        mHasFadeOut = !show;
+    }
+
     private void finishResizeForMenu(Rect destinationBounds) {
         mPipMenuController.movePipMenu(null, null, destinationBounds);
         mPipMenuController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 3403fb5..02e713d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -123,6 +123,10 @@
     public void forceFinishTransition() {
     }
 
+    /** Called when the fixed rotation started. */
+    public void onFixedRotationStarted() {
+    }
+
     public PipTransitionController(PipBoundsState pipBoundsState,
             PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
             PipAnimationController pipAnimationController, Transitions transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 5996acd..5069180 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -289,7 +290,6 @@
         if (DEBUG) Log.d(TAG, "checkIfPinnedTaskAppeared(), task=" + pinnedTask);
         if (pinnedTask == null || pinnedTask.topActivity == null) return;
         mPinnedTaskId = pinnedTask.taskId;
-        setState(STATE_PIP);
 
         mPipMediaController.onActivityPinned();
         mPipNotificationController.show(pinnedTask.topActivity.getPackageName());
@@ -326,6 +326,9 @@
 
     @Override
     public void onPipTransitionFinished(int direction) {
+        if (PipAnimationController.isInPipDirection(direction) && mState == STATE_NO_PIP) {
+            setState(STATE_PIP);
+        }
         if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState));
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index d022ec1..13137fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -97,9 +97,8 @@
      * Start a pair of intent and task using legacy transition system.
      */
     oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
-            in Intent fillInIntent, int taskId, boolean intentFirst, in Bundle mainOptions,
-            in Bundle sideOptions, int sidePosition, float splitRatio,
-            in RemoteAnimationAdapter adapter) = 12;
+            in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
+            int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter) = 12;
 
     /**
      * Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 990b53a..e88eef9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -349,17 +349,20 @@
                     RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                     IRemoteAnimationFinishedCallback finishedCallback,
                     SurfaceControl.Transaction t) {
-                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
-
-                if (apps != null) {
-                    for (int i = 0; i < apps.length; ++i) {
-                        if (apps[i].mode == MODE_OPENING) {
-                            t.show(apps[i].leash);
-                        }
-                    }
+                if (apps == null || apps.length == 0) {
+                    // Do nothing when the animation was cancelled.
+                    t.apply();
+                    return;
                 }
 
+                mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+                for (int i = 0; i < apps.length; ++i) {
+                    if (apps[i].mode == MODE_OPENING) {
+                        t.show(apps[i].leash);
+                    }
+                }
                 t.apply();
+
                 if (finishedCallback != null) {
                     try {
                         finishedCallback.onAnimationFinished();
@@ -642,14 +645,13 @@
 
         @Override
         public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
-                Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions,
-                Bundle sideOptions, int sidePosition, float splitRatio,
-                RemoteAnimationAdapter adapter) {
+                Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
+                int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
             executeRemoteCallWithTaskPermission(mController,
                     "startIntentAndTaskWithLegacyTransition", (controller) ->
                             controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
-                                    pendingIntent, fillInIntent, taskId, intentFirst, mainOptions,
-                                    sideOptions, sidePosition, splitRatio, adapter));
+                                    pendingIntent, fillInIntent, taskId, mainOptions, sideOptions,
+                                    sidePosition, splitRatio, adapter));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 029d073..81dacdb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -373,6 +373,23 @@
     void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
             int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
             float splitRatio, RemoteAnimationAdapter adapter) {
+        startWithLegacyTransition(mainTaskId, sideTaskId, null /* pendingIntent */,
+                null /* fillInIntent */, mainOptions, sideOptions, sidePosition, splitRatio,
+                adapter);
+    }
+
+    /** Start an intent and a task ordered by {@code intentFirst}. */
+    void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
+            int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+        startWithLegacyTransition(taskId, INVALID_TASK_ID, pendingIntent, fillInIntent,
+                mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+    }
+
+    private void startWithLegacyTransition(int mainTaskId, int sideTaskId,
+            @Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+            @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
+            @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
         // Init divider first to make divider leash for remote animation target.
         mSplitLayout.init();
         // Set false to avoid record new bounds with old task still on top;
@@ -466,124 +483,19 @@
         addActivityOptions(sideOptions, mSideStage);
 
         // Add task launch requests
-        wct.startTask(mainTaskId, mainOptions);
-        wct.startTask(sideTaskId, sideOptions);
+        if (pendingIntent != null && fillInIntent != null) {
+            wct.startTask(mainTaskId, mainOptions);
+            wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
+        } else {
+            wct.startTask(mainTaskId, mainOptions);
+            wct.startTask(sideTaskId, sideOptions);
+        }
 
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
         mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
     }
 
-    /** Start an intent and a task ordered by {@code intentFirst}. */
-    void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
-            int taskId, boolean intentFirst, @Nullable Bundle mainOptions,
-            @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter) {
-        // TODO: try pulling the first chunk of this method into a method so that it can be shared
-        // with startTasksWithLegacyTransition. So far attempts to do so result in failure in split.
-
-        // Init divider first to make divider leash for remote animation target.
-        mSplitLayout.init();
-        // Set false to avoid record new bounds with old task still on top;
-        mShouldUpdateRecents = false;
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-        final WindowContainerTransaction evictWct = new WindowContainerTransaction();
-        prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
-        prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
-        // Need to add another wrapper here in shell so that we can inject the divider bar
-        // and also manage the process elevation via setRunningRemote
-        IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
-            @Override
-            public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-                    RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers,
-                    RemoteAnimationTarget[] nonApps,
-                    final IRemoteAnimationFinishedCallback finishedCallback) {
-                RemoteAnimationTarget[] augmentedNonApps =
-                        new RemoteAnimationTarget[nonApps.length + 1];
-                for (int i = 0; i < nonApps.length; ++i) {
-                    augmentedNonApps[i] = nonApps[i];
-                }
-                augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
-
-                IRemoteAnimationFinishedCallback wrapCallback =
-                        new IRemoteAnimationFinishedCallback.Stub() {
-                            @Override
-                            public void onAnimationFinished() throws RemoteException {
-                                mShouldUpdateRecents = true;
-                                mSyncQueue.queue(evictWct);
-                                mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
-                                finishedCallback.onAnimationFinished();
-                            }
-                        };
-                try {
-                    try {
-                        ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
-                                adapter.getCallingApplication());
-                    } catch (SecurityException e) {
-                        Slog.e(TAG, "Unable to boost animation thread. This should only happen"
-                                + " during unit tests");
-                    }
-                    adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
-                            augmentedNonApps, wrapCallback);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error starting remote animation", e);
-                }
-            }
-
-            @Override
-            public void onAnimationCancelled() {
-                mShouldUpdateRecents = true;
-                mSyncQueue.queue(evictWct);
-                mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
-                try {
-                    adapter.getRunner().onAnimationCancelled();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error starting remote animation", e);
-                }
-            }
-        };
-        RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
-                wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
-
-        if (mainOptions == null) {
-            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
-        } else {
-            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
-            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-            mainOptions = mainActivityOptions.toBundle();
-        }
-
-        sideOptions = sideOptions != null ? sideOptions : new Bundle();
-        setSideStagePosition(sidePosition, wct);
-
-        mSplitLayout.setDivideRatio(splitRatio);
-        if (mMainStage.isActive()) {
-            mMainStage.moveToTop(getMainStageBounds(), wct);
-        } else {
-            // Build a request WCT that will launch both apps such that task 0 is on the main stage
-            // while task 1 is on the side stage.
-            mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
-        }
-        mSideStage.moveToTop(getSideStageBounds(), wct);
-
-        // Make sure the launch options will put tasks in the corresponding split roots
-        addActivityOptions(mainOptions, mMainStage);
-        addActivityOptions(sideOptions, mSideStage);
-
-        // Add task launch requests
-        if (intentFirst) {
-            wct.sendPendingIntent(pendingIntent, fillInIntent, mainOptions);
-            wct.startTask(taskId, sideOptions);
-        } else {
-            wct.startTask(taskId, mainOptions);
-            wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
-        }
-
-        // Using legacy transitions, so we can't use blast sync since it conflicts.
-        mTaskOrganizer.applyTransaction(wct);
-    }
-
     /**
      * Collects all the current child tasks of a specific split and prepares transaction to evict
      * them to display.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 3442699..61cbf6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -364,6 +364,12 @@
                 final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
                 final SplashScreenView contentView = viewSupplier.get();
                 record.mBGColor = contentView.getInitBackgroundColor();
+            } else {
+                // release the icon view host
+                final SplashScreenView contentView = viewSupplier.get();
+                if (contentView.getSurfaceHost() != null) {
+                    SplashScreenView.releaseIconHost(contentView.getSurfaceHost());
+                }
             }
         } catch (RuntimeException e) {
             // don't crash if something else bad happens, for example a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 86b73fc..efb52a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -74,6 +74,8 @@
     /** Set to {@code true} to enable shell transitions. */
     public static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.debug.shell_transit", false);
+    public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
+            && SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false);
 
     /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
     public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 556742e..574a9f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -26,16 +26,8 @@
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
         <option name="hidden-api-checks" value="false" />
-        <option name="device-listeners"
-                value="com.android.server.wm.flicker.TraceFileReadyListener" />
     </test>
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="(\w)+\.winscope" />
-        <option name="pull-pattern-keys" value="(\w)+\.mp4" />
-        <option name="collect-on-run-ended-only" value="false" />
-        <option name="clean-up" value="true" />
-    </metrics_collector>
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/sdcard/flicker" />
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 65eb9aa..57bcbc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -60,7 +62,18 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                         composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
+                val waitConditions = mutableListOf(
+                    WindowManagerConditionsFactory.isWindowVisible(primaryApp.component),
+                    WindowManagerConditionsFactory.isLayerVisible(primaryApp.component),
+                    WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+                nonResizeableApp?.let {
+                    waitConditions.add(
+                        WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component))
+                    waitConditions.add(
+                        WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component))
+                }
+                wmHelper.waitFor(*waitConditions.toTypedArray())
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index a20a201..a57d3e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,8 +16,8 @@
 
 package com.android.wm.shell.flicker.bubble
 
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.annotation.Group4
@@ -25,8 +25,8 @@
 import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import org.junit.Assume
 import org.junit.Before
-import org.junit.runner.RunWith
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
 /**
@@ -57,7 +57,7 @@
             }
         }
 
-    @Presubmit
+    @FlakyTest(bugId = 218642026)
     @Test
     open fun testAppIsAlwaysVisible() {
         testSpec.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 0f00ede..cc5b9f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -62,7 +62,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeShown()) { "IME did not appear" }
+            wmHelper.waitImeShown()
         }
     }
 
@@ -79,7 +79,7 @@
             if (wmHelper == null) {
                 uiDevice.waitForIdle()
             } else {
-                require(wmHelper.waitImeGone()) { "IME did did not close" }
+                wmHelper.waitImeGone()
             }
         } else {
             // While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 7e232ea..e9d438a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -58,17 +58,27 @@
         }
     }
 
-    /** {@inheritDoc}  */
-    override fun launchViaIntent(
+    /**
+     * Launches the app through an intent instead of interacting with the launcher and waits
+     * until the app window is in PIP mode
+     */
+    @JvmOverloads
+    fun launchViaIntentAndWaitForPip(
         wmHelper: WindowManagerStateHelper,
-        expectedWindowName: String,
-        action: String?,
+        expectedWindowName: String = "",
+        action: String? = null,
         stringExtras: Map<String, String>
     ) {
-        super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitPipShown()
+        launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras,
+            waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition))
     }
 
+    /**
+     * Expand the PIP window back to full screen via intent and wait until the app is visible
+     */
+    fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
+        launchViaIntentAndWaitShown(wmHelper)
+
     private fun focusOnObject(selector: BySelector): Boolean {
         // We expect all the focusable UI elements to be arranged in a way so that it is possible
         // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 467cadc..274d34b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -65,7 +64,18 @@
      * Defines the transition used to run the test
      */
     override val transition: FlickerBuilder.() -> Unit
-        get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
+        get() = {
+            setupAndTeardown(this)
+            setup {
+                eachRun {
+                    pipApp.launchViaIntent(wmHelper)
+                }
+            }
+            teardown {
+                eachRun {
+                    pipApp.exit(wmHelper)
+                }
+            }
             transitions {
                 pipApp.clickEnterPipButton(wmHelper)
             }
@@ -167,7 +177,7 @@
      * Checks that the focus changes between the [pipApp] window and the launcher when
      * closing the pip window
      */
-    @Postsubmit
+    @Presubmit
     @Test
     fun focusChanges() {
         testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 173140d..0b4bc76 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -94,15 +93,4 @@
                 .isVisible(LAUNCHER_COMPONENT)
         }
     }
-
-    /**
-     * Checks that the focus doesn't change between windows during the transition
-     */
-    @Postsubmit
-    @Test
-    open fun focusDoesNotChange() {
-        testSpec.assertEventLog {
-            this.focusDoesNotChange()
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 931d060..e2d0834 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -54,6 +54,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
+@FlakyTest(bugId = 219750830)
 class ExitPipViaExpandButtonClickTest(
     testSpec: FlickerTestParameter
 ) : ExitPipToAppTransition(testSpec) {
@@ -73,7 +74,7 @@
                 // This will bring PipApp to fullscreen
                 pipApp.expandPipWindowToApp(wmHelper)
                 // Wait until the other app is no longer visible
-                wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+                wmHelper.waitForSurfaceAppeared(testApp.component)
             }
         }
 
@@ -101,4 +102,4 @@
                     supportedRotations = listOf(Surface.ROTATION_0), repetitions = 3)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index e00d749..3fe6f02 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -72,9 +72,9 @@
             }
             transitions {
                 // This will bring PipApp to fullscreen
-                pipApp.launchViaIntent(wmHelper)
+                pipApp.exitPipToFullScreenViaIntent(wmHelper)
                 // Wait until the other app is no longer visible
-                wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+                wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 5214daa0..9c095a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -72,10 +73,17 @@
     @Test
     override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
-    /** {@inheritDoc}  */
-    @FlakyTest(bugId = 215869110)
+    /**
+     * Checks that the focus changes between the pip menu window and the launcher when clicking the
+     * dismiss button on pip menu to close the pip window.
+     */
+    @Presubmit
     @Test
-    override fun focusDoesNotChange() = super.focusDoesNotChange()
+    fun focusDoesNotChange() {
+        testSpec.assertEventLog {
+            this.focusChanges("PipMenuView", "NexusLauncherActivity")
+        }
+    }
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 332bba6a..ab07ede 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -81,6 +82,17 @@
     @Test
     override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
 
+    /**
+     * Checks that the focus doesn't change between windows during the transition
+     */
+    @Presubmit
+    @Test
+    fun focusDoesNotChange() {
+        testSpec.assertEventLog {
+            this.focusDoesNotChange()
+        }
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 07c3b15..5fc80db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -16,10 +16,9 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -149,7 +148,7 @@
     /**
      * Checks that the focus doesn't change between windows during the transition
      */
-    @Postsubmit
+    @FlakyTest(bugId = 216306753)
     @Test
     fun focusDoesNotChange() {
         testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index e91bef1..87e927f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -24,7 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.traces.region.RegionSubject
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -55,9 +58,14 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
-class MovePipDownShelfHeightChangeTest(
+open class MovePipDownShelfHeightChangeTest(
     testSpec: FlickerTestParameter
 ) : MovePipShelfHeightTransition(testSpec) {
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt
new file mode 100644
index 0000000..0ff260b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest_ShellTransit.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip movement with Launcher shelf height change (decrease).
+ *
+ * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
+ *
+ * Actions:
+ *     Launch [pipApp] in pip mode
+ *     Launch [testApp]
+ *     Press home
+ *     Check if pip window moves down (visually)
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [PipTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+@FlakyTest(bugId = 219693385)
+class MovePipDownShelfHeightChangeTest_ShellTransit(
+    testSpec: FlickerTestParameter
+) : MovePipDownShelfHeightChangeTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 7e66cd3..388b5e0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,15 +16,18 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.traces.region.RegionSubject
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,6 +61,11 @@
 class MovePipUpShelfHeightChangeTest(
     testSpec: FlickerTestParameter
 ) : MovePipShelfHeightTransition(testSpec) {
+    @Before
+    fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index bb66f7b..654fa4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -122,15 +122,14 @@
 
             setup {
                 test {
-                    removeAllTasksButHome()
                     if (!eachRun) {
-                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
                         wmHelper.waitPipShown()
                     }
                 }
                 eachRun {
                     if (eachRun) {
-                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
                         wmHelper.waitPipShown()
                     }
                 }
@@ -145,7 +144,6 @@
                     if (!eachRun) {
                         pipApp.exit(wmHelper)
                     }
-                    removeAllTasksButHome()
                 }
             }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index c5be485..29e40be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -52,6 +52,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -81,7 +82,8 @@
     private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
     private @Mock SyncTransactionQueue mMockSyncQueue;
     private @Mock ShellExecutor mMockExecutor;
-    private @Mock CompatUIWindowManager mMockLayout;
+    private @Mock CompatUIWindowManager mMockCompatLayout;
+    private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -91,16 +93,26 @@
         MockitoAnnotations.initMocks(this);
 
         doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
-        doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
-        doReturn(TASK_ID).when(mMockLayout).getTaskId();
-        doReturn(true).when(mMockLayout).createLayout(anyBoolean());
-        doReturn(true).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(DISPLAY_ID).when(mMockCompatLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockCompatLayout).getTaskId();
+        doReturn(true).when(mMockCompatLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(DISPLAY_ID).when(mMockLetterboxEduLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
+        doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
         mController = new CompatUIController(mContext, mMockDisplayController,
                 mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
-                return mMockLayout;
+                return mMockCompatLayout;
+            }
+
+            @Override
+            LetterboxEduWindowManager createLetterboxEduWindowManager(Context context,
+                    TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+                return mMockLetterboxEduLayout;
             }
         };
         spyOn(mController);
@@ -121,68 +133,95 @@
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        // Verify that the compat controls are updated with new size compat info.
-        clearInvocations(mMockLayout);
+        // Verify that the compat controls and letterbox education are updated with new size compat
+        // info.
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
 
-        // Verify that compat controls are removed with null task listener.
-        clearInvocations(mMockLayout);
+        // Verify that compat controls and letterbox education are removed with null task listener.
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
                 /* taskListener= */ null);
 
-        verify(mMockLayout).release();
+        verify(mMockCompatLayout).release();
+        verify(mMockLetterboxEduLayout).release();
     }
 
     @Test
     public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
-        doReturn(false).when(mMockLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the layout is created again.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
     @Test
     public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
-        doReturn(false).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
 
         // Verify that the layout is created again.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
 
@@ -204,14 +243,16 @@
 
         mController.onDisplayRemoved(DISPLAY_ID + 1);
 
-        verify(mMockLayout, never()).release();
+        verify(mMockCompatLayout, never()).release();
+        verify(mMockLetterboxEduLayout, never()).release();
         verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
                 any());
 
         mController.onDisplayRemoved(DISPLAY_ID);
 
         verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
-        verify(mMockLayout).release();
+        verify(mMockCompatLayout).release();
+        verify(mMockLetterboxEduLayout).release();
     }
 
     @Test
@@ -221,11 +262,13 @@
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
 
-        verify(mMockLayout, never()).updateDisplayLayout(any());
+        verify(mMockCompatLayout, never()).updateDisplayLayout(any());
+        verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
 
-        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -242,104 +285,125 @@
                 mOnInsetsChangedListenerCaptor.capture());
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
 
-        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
 
         // No update if the insets state is the same.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
-        verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
-    public void testChangeButtonVisibilityOnImeShowHide() {
+    public void testChangeLayoutsVisibilityOnImeShowHide() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         // Verify that the restart button is hidden after IME is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
 
         // Verify button is shown after IME is hidden.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
+    public void testChangeLayoutsVisibilityOnKeyguardOccludedChanged() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         // Verify that the restart button is hidden after keyguard becomes occluded.
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is occluded.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
 
         // Verify button is shown after keyguard becomes not occluded.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+    public void testLayoutsRemainHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout, times(2)).updateVisibility(false);
+        verify(mMockCompatLayout, times(2)).updateVisibility(false);
+        verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
 
         // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button is shown after IME is not showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
+    public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsOccluded() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout, times(2)).updateVisibility(false);
+        verify(mMockCompatLayout, times(2)).updateVisibility(false);
+        verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
 
         // Verify button remains hidden after IME is hidden since keyguard is occluded.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button is shown after keyguard becomes not occluded.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 352805b..7d3e718 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -77,8 +78,7 @@
         mWindowManager = new CompatUIWindowManager(mContext,
                 createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
                 mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
-                /* hasShownCameraCompatHint= */ false);
+                new DisplayLayout(), new CompatUIHintsState());
 
         mLayout = (CompatUILayout)
                 LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index f9cfd12..e79b803 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -51,6 +51,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,8 +86,7 @@
         mWindowManager = new CompatUIWindowManager(mContext,
                 createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
                 mSyncTransactionQueue, mCallback, mTaskListener,
-                new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
-                /* hasShownCameraCompatHint= */ false);
+                new DisplayLayout(), new CompatUIHintsState());
 
         spyOn(mWindowManager);
         doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -102,7 +102,7 @@
         verify(mWindowManager, never()).inflateLayout();
 
         // Doesn't create hint popup.
-        mWindowManager.mShouldShowSizeCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = true;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
@@ -113,14 +113,14 @@
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
         mWindowManager.release();
-        mWindowManager.mShouldShowSizeCompatHint = true;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = false;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
         assertNotNull(mLayout);
         verify(mLayout).setRestartButtonVisibility(/* show= */ true);
         verify(mLayout).setSizeCompatHintVisibility(/* show= */ true);
-        assertFalse(mWindowManager.mShouldShowSizeCompatHint);
+        assertTrue(mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint);
 
         // Returns false and doesn't create layout if has Size Compat is false.
         clearInvocations(mWindowManager);
@@ -140,7 +140,7 @@
         verify(mWindowManager, never()).inflateLayout();
 
         // Doesn't create hint popup.
-        mWindowManager.mShouldShowCameraCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
@@ -151,14 +151,14 @@
         clearInvocations(mWindowManager);
         clearInvocations(mLayout);
         mWindowManager.release();
-        mWindowManager.mShouldShowCameraCompatHint = true;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = false;
         assertTrue(mWindowManager.createLayout(/* canShow= */ true));
 
         verify(mWindowManager).inflateLayout();
         assertNotNull(mLayout);
         verify(mLayout).setCameraControlVisibility(/* show= */ true);
         verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
-        assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+        assertTrue(mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint);
 
         // Returns false and doesn't create layout if Camera Compat state is hidden
         clearInvocations(mWindowManager);
@@ -411,7 +411,7 @@
     public void testOnRestartButtonLongClicked_showHint() {
        // Not create hint popup.
         mWindowManager.mHasSizeCompat = true;
-        mWindowManager.mShouldShowSizeCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownSizeCompatHint = true;
         mWindowManager.createLayout(/* canShow= */ true);
 
         verify(mWindowManager).inflateLayout();
@@ -423,10 +423,10 @@
     }
 
     @Test
-    public void testOnCamerControlLongClicked_showHint() {
+    public void testOnCameraControlLongClicked_showHint() {
        // Not create hint popup.
         mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-        mWindowManager.mShouldShowCameraCompatHint = false;
+        mWindowManager.mCompatUIHintsState.mHasShownCameraCompatHint = true;
         mWindowManager.createLayout(/* canShow= */ true);
 
         verify(mWindowManager).inflateLayout();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
new file mode 100644
index 0000000..00e4938
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterboxedu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxEduDialogLayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:LetterboxEduDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduDialogLayoutTest extends ShellTestCase {
+
+    @Mock
+    private Runnable mDismissCallback;
+
+    private LetterboxEduDialogLayout mLayout;
+    private View mDismissButton;
+    private View mDialogContainer;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mLayout = (LetterboxEduDialogLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.letterbox_education_dialog_layout,
+                        null);
+        mDismissButton = mLayout.findViewById(R.id.letterbox_education_dialog_dismiss_button);
+        mDialogContainer = mLayout.findViewById(R.id.letterbox_education_dialog_container);
+        mLayout.setDismissOnClickListener(mDismissCallback);
+    }
+
+    @Test
+    public void testOnFinishInflate() {
+        assertEquals(mLayout.getDialogContainer(),
+                mLayout.findViewById(R.id.letterbox_education_dialog_container));
+        assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
+        assertEquals(mLayout.getBackground().getAlpha(), 0);
+    }
+
+    @Test
+    public void testOnDismissButtonClicked() {
+        assertTrue(mDismissButton.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnBackgroundClicked() {
+        assertTrue(mLayout.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnDialogContainerClicked() {
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testSetDismissOnClickListenerNull() {
+        mLayout.setDismissOnClickListener(null);
+
+        assertFalse(mDismissButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertFalse(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
new file mode 100644
index 0000000..0509dd3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterboxedu;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.SurfaceControlViewHost;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.After;
+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;
+
+/**
+ * Tests for {@link LetterboxEduWindowManager}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:LetterboxEduWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduWindowManagerTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    private static final int TASK_WIDTH = 200;
+    private static final int TASK_HEIGHT = 100;
+    private static final int DISPLAY_CUTOUT_TOP = 5;
+    private static final int DISPLAY_CUTOUT_BOTTOM = 10;
+    private static final int DISPLAY_CUTOUT_HORIZONTAL = 20;
+
+    @Captor
+    private ArgumentCaptor<WindowManager.LayoutParams> mWindowAttrsCaptor;
+    @Captor
+    private ArgumentCaptor<Runnable> mEndCallbackCaptor;
+
+    @Mock private LetterboxEduAnimationController mAnimationController;
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private Runnable mOnDismissCallback;
+
+    private SharedPreferences mSharedPreferences;
+    private String mPrefKey;
+    @Nullable
+    private Boolean mInitialPrefValue = null;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mSharedPreferences = mContext.getSharedPreferences(
+                LetterboxEduWindowManager.HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
+                Context.MODE_PRIVATE);
+        mPrefKey = String.valueOf(mContext.getUserId());
+        if (mSharedPreferences.contains(mPrefKey)) {
+            mInitialPrefValue = mSharedPreferences.getBoolean(mPrefKey, /* default= */ false);
+            mSharedPreferences.edit().remove(mPrefKey).apply();
+        }
+    }
+
+    @After
+    public void tearDown() {
+        SharedPreferences.Editor editor = mSharedPreferences.edit();
+        if (mInitialPrefValue == null) {
+            editor.remove(mPrefKey);
+        } else {
+            editor.putBoolean(mPrefKey, mInitialPrefValue);
+        }
+        editor.apply();
+    }
+
+    @Test
+    public void testCreateLayout_notEligible_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_alreadyShownToUser_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+        mSharedPreferences.edit().putBoolean(mPrefKey, true).apply();
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
+                true, /* isTaskbarEduShowing= */ true);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_canShowFalse_returnsTrueButDoesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ false));
+
+        assertFalse(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_canShowTrue_createsLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+        assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+        verify(mViewHost).setView(eq(layout), mWindowAttrsCaptor.capture());
+        verifyLayout(layout, mWindowAttrsCaptor.getValue(), /* expectedWidth= */ TASK_WIDTH,
+                /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */ DISPLAY_CUTOUT_TOP,
+                /* expectedExtraBottomMargin= */ DISPLAY_CUTOUT_BOTTOM);
+
+        // Clicking the layout does nothing until enter animation is done.
+        layout.performClick();
+        verify(mAnimationController, never()).startExitAnimation(any(), any());
+
+        verifyAndFinishEnterAnimation(layout);
+
+        // Exit animation should start following a click on the layout.
+        layout.performClick();
+
+        // Window manager isn't released until exit animation is done.
+        verify(windowManager, never()).release();
+
+        // Verify multiple clicks are ignored.
+        layout.performClick();
+
+        verifyAndFinishExitAnimation(layout);
+
+        verify(windowManager).release();
+        verify(mOnDismissCallback).run();
+    }
+
+    @Test
+    public void testUpdateCompatInfo_updatesLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+
+        assertTrue(windowManager.updateCompatInfo(
+                createTaskInfo(/* eligible= */ true, new Rect(50, 25, 150, 75)),
+                mTaskListener, /* canShow= */ true));
+
+        verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
+                /* expectedHeight= */ 50, /* expectedExtraTopMargin= */ 0,
+                /* expectedExtraBottomMargin= */ 0);
+        verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+        assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+
+        // Window manager should be released (without animation) when eligible becomes false.
+        assertFalse(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ false),
+                mTaskListener, /* canShow= */ true));
+
+        verify(windowManager).release();
+        verify(mOnDismissCallback, never()).run();
+        verify(mAnimationController, never()).startExitAnimation(any(), any());
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testUpdateCompatInfo_notEligibleUntilUpdate_createsLayoutAfterUpdate() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+        assertNull(windowManager.mLayout);
+
+        assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+                mTaskListener, /* canShow= */ true));
+
+        assertNotNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testUpdateCompatInfo_canShowFalse_doesNothing() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ false));
+        assertNull(windowManager.mLayout);
+
+        assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+                mTaskListener, /* canShow= */ false));
+
+        assertNull(windowManager.mLayout);
+        verify(mViewHost, never()).relayout(any());
+    }
+
+    @Test
+    public void testUpdateDisplayLayout_updatesLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+
+        int newDisplayCutoutTop = DISPLAY_CUTOUT_TOP + 7;
+        int newDisplayCutoutBottom = DISPLAY_CUTOUT_BOTTOM + 9;
+        windowManager.updateDisplayLayout(createDisplayLayout(
+                Insets.of(DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutTop,
+                        DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutBottom)));
+
+        verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ TASK_WIDTH,
+                /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */
+                newDisplayCutoutTop, /* expectedExtraBottomMargin= */ newDisplayCutoutBottom);
+        verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+        assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+    }
+
+    @Test
+    public void testRelease_animationIsCancelled() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        windowManager.release();
+
+        verify(mAnimationController).cancelAnimation();
+    }
+
+    private void verifyLayout(LetterboxEduDialogLayout layout, ViewGroup.LayoutParams params,
+            int expectedWidth, int expectedHeight, int expectedExtraTopMargin,
+            int expectedExtraBottomMargin) {
+        assertThat(params.width).isEqualTo(expectedWidth);
+        assertThat(params.height).isEqualTo(expectedHeight);
+        MarginLayoutParams dialogParams =
+                (MarginLayoutParams) layout.getDialogContainer().getLayoutParams();
+        int verticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_education_dialog_margin);
+        assertThat(dialogParams.topMargin).isEqualTo(verticalMargin + expectedExtraTopMargin);
+        assertThat(dialogParams.bottomMargin).isEqualTo(verticalMargin + expectedExtraBottomMargin);
+    }
+
+    private void verifyAndFinishEnterAnimation(LetterboxEduDialogLayout layout) {
+        verify(mAnimationController).startEnterAnimation(eq(layout), mEndCallbackCaptor.capture());
+        mEndCallbackCaptor.getValue().run();
+    }
+
+    private void verifyAndFinishExitAnimation(LetterboxEduDialogLayout layout) {
+        verify(mAnimationController).startExitAnimation(eq(layout), mEndCallbackCaptor.capture());
+        mEndCallbackCaptor.getValue().run();
+    }
+
+    private LetterboxEduWindowManager createWindowManager(boolean eligible) {
+        return createWindowManager(eligible, /* isTaskbarEduShowing= */ false);
+    }
+
+    private LetterboxEduWindowManager createWindowManager(boolean eligible,
+            boolean isTaskbarEduShowing) {
+        LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
+                createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener,
+                createDisplayLayout(), mOnDismissCallback, mAnimationController);
+
+        spyOn(windowManager);
+        doReturn(mViewHost).when(windowManager).createSurfaceViewHost();
+        doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing();
+
+        return windowManager;
+    }
+
+    private DisplayLayout createDisplayLayout() {
+        return createDisplayLayout(
+                Insets.of(DISPLAY_CUTOUT_HORIZONTAL, DISPLAY_CUTOUT_TOP, DISPLAY_CUTOUT_HORIZONTAL,
+                        DISPLAY_CUTOUT_BOTTOM));
+    }
+
+    private DisplayLayout createDisplayLayout(Insets insets) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = TASK_WIDTH;
+        displayInfo.logicalHeight = TASK_HEIGHT;
+        displayInfo.displayCutout = new DisplayCutout(
+                insets, null, null, null, null);
+        return new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+    }
+
+    private static TaskInfo createTaskInfo(boolean eligible) {
+        return createTaskInfo(eligible, new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT));
+    }
+
+    private static TaskInfo createTaskInfo(boolean eligible, Rect bounds) {
+        ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+        taskInfo.taskId = TASK_ID;
+        taskInfo.topActivityEligibleForLetterboxEducation = eligible;
+        taskInfo.configuration.windowConfiguration.setBounds(bounds);
+        return taskInfo;
+    }
+}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 63b831d..c8cc3526 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -33,6 +33,7 @@
 
 cc_defaults {
     name: "libandroidfw_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Werror",
         "-Wunreachable-code",
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 921877dc..fac2fa4 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -288,12 +288,12 @@
 
 template <typename TChar>
 inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs == lhs;
+  return BasicStringPiece<TChar>(lhs) == rhs;
 }
 
 template <typename TChar>
 inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs != lhs;
+  return BasicStringPiece<TChar>(lhs) != rhs;
 }
 
 }  // namespace android
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index 76ea2d5..c13c800 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -134,6 +134,21 @@
     GraphicsJNI::irect_to_jrect(ir, env, bounds);
 }
 
+// Regular JNI
+static jlong nGetExtent(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start,
+                        jint end) {
+    ScopedCharArrayRO text(env, javaText);
+    const minikin::U16StringPiece textBuffer(text.get(), text.size());
+    const minikin::Range range(start, end);
+
+    minikin::MinikinExtent extent = toMeasuredParagraph(ptr)->getExtent(textBuffer, range);
+
+    int32_t ascent = SkScalarRoundToInt(extent.ascent);
+    int32_t descent = SkScalarRoundToInt(extent.descent);
+
+    return (((jlong)(ascent)) << 32) | ((jlong)descent);
+}
+
 // CriticalNative
 static jlong nGetReleaseFunc(CRITICAL_JNI_PARAMS) {
     return toJLong(&releaseMeasuredParagraph);
@@ -153,12 +168,13 @@
 };
 
 static const JNINativeMethod gMTMethods[] = {
-    // MeasuredParagraph native functions.
-    {"nGetWidth", "(JII)F", (void*) nGetWidth},  // Critical Natives
-    {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds},  // Regular JNI
-    {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
-    {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage},  // Critical Native
-    {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt},  // Critical Native
+        // MeasuredParagraph native functions.
+        {"nGetWidth", "(JII)F", (void*)nGetWidth},                             // Critical Natives
+        {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*)nGetBounds},  // Regular JNI
+        {"nGetExtent", "(J[CII)J", (void*)nGetExtent},                         // Regular JNI
+        {"nGetReleaseFunc", "()J", (void*)nGetReleaseFunc},                    // Critical Natives
+        {"nGetMemoryUsage", "(J)I", (void*)nGetMemoryUsage},                   // Critical Native
+        {"nGetCharWidthAt", "(JI)F", (void*)nGetCharWidthAt},                  // Critical Native
 };
 
 int register_android_graphics_text_MeasuredText(JNIEnv* env) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f627a3c..01b956c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -152,8 +152,8 @@
     ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
 
     virtual void requestNextVsync() override {
-        AChoreographer_postExtendedFrameCallback(
-                mRenderThread->mChoreographer, RenderThread::extendedFrameCallback, mRenderThread);
+        AChoreographer_postVsyncCallback(mRenderThread->mChoreographer,
+                                         RenderThread::extendedFrameCallback, mRenderThread);
     }
 
     virtual void drainPendingEvents() override {
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index 29888e1..f140c68 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -144,8 +144,8 @@
     private final ClockInfo mClockInfo;
     private final double mIonoDelayMeters;
     private final double mTropoDelayMeters;
-    private final int mTimeOfClock;
-    private final int mTimeOfEphemeris;
+    private final long mTimeOfClock;
+    private final long mTimeOfEphemeris;
     private final int mIssueOfDataClock;
     private final int mIssueOfDataEphemeris;
     @EphemerisSource
@@ -457,8 +457,8 @@
             @Nullable ClockInfo clockInfo,
             double ionoDelayMeters,
             double tropoDelayMeters,
-            int timeOfClock,
-            int timeOfEphemeris,
+            long timeOfClock,
+            long timeOfEphemeris,
             int issueOfDataClock,
             int issueOfDataEphemeris,
             @EphemerisSource int ephemerisSource) {
@@ -547,26 +547,28 @@
     /**
      * Time of Clock.
      *
-     * <p>This is defined in GPS ICD200 documentation (e.g.,
-     * <a href="https://www.gps.gov/technical/icwg/IS-GPS-200H.pdf"></a>).
+     * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+     *
+     * <p>The value is not encoded as in GPS ICD200 documentation.
      *
      * <p>This field is valid if {@link #hasTimeOfClock()} is true.
      */
-    @IntRange(from = 0, to = 604784)
-    public int getTimeOfClock() {
+    @IntRange(from = 0)
+    public long getTimeOfClock() {
         return mTimeOfClock;
     }
 
     /**
      * Time of ephemeris.
      *
-     * <p>This is defined in GPS ICD200 documentation (e.g.,
-     * <a href="https://www.gps.gov/technical/icwg/IS-GPS-200H.pdf"></a>).
+     * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+     *
+     * <p>The value is not encoded as in GPS ICD200 documentation.
      *
      * <p>This field is valid if {@link #hasTimeOfEphemeris()} is true.
      */
-    @IntRange(from = 0, to = 604784)
-    public int getTimeOfEphemeris() {
+    @IntRange(from = 0)
+    public long getTimeOfEphemeris() {
         return mTimeOfEphemeris;
     }
 
@@ -630,8 +632,8 @@
                             android.location.SatellitePvt.ClockInfo.class);
                     double ionoDelayMeters = in.readDouble();
                     double tropoDelayMeters = in.readDouble();
-                    int toc = in.readInt();
-                    int toe = in.readInt();
+                    long toc = in.readLong();
+                    long toe = in.readLong();
                     int iodc = in.readInt();
                     int iode = in.readInt();
                     int ephemerisSource = in.readInt();
@@ -669,8 +671,8 @@
         parcel.writeParcelable(mClockInfo, flags);
         parcel.writeDouble(mIonoDelayMeters);
         parcel.writeDouble(mTropoDelayMeters);
-        parcel.writeInt(mTimeOfClock);
-        parcel.writeInt(mTimeOfEphemeris);
+        parcel.writeLong(mTimeOfClock);
+        parcel.writeLong(mTimeOfEphemeris);
         parcel.writeInt(mIssueOfDataClock);
         parcel.writeInt(mIssueOfDataEphemeris);
         parcel.writeInt(mEphemerisSource);
@@ -707,8 +709,8 @@
         @Nullable private ClockInfo mClockInfo;
         private double mIonoDelayMeters;
         private double mTropoDelayMeters;
-        private int mTimeOfClock;
-        private int mTimeOfEphemeris;
+        private long mTimeOfClock;
+        private long mTimeOfEphemeris;
         private int mIssueOfDataClock;
         private int mIssueOfDataEphemeris;
         @EphemerisSource
@@ -721,8 +723,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setPositionEcef(
-                @NonNull PositionEcef positionEcef) {
+        public Builder setPositionEcef(@NonNull PositionEcef positionEcef) {
             mPositionEcef = positionEcef;
             updateFlags();
             return this;
@@ -735,8 +736,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setVelocityEcef(
-                @NonNull VelocityEcef velocityEcef) {
+        public Builder setVelocityEcef(@NonNull VelocityEcef velocityEcef) {
             mVelocityEcef = velocityEcef;
             updateFlags();
             return this;
@@ -749,8 +749,7 @@
          * @return builder object
          */
         @NonNull
-        public Builder setClockInfo(
-                @NonNull ClockInfo clockInfo) {
+        public Builder setClockInfo(@NonNull ClockInfo clockInfo) {
             mClockInfo = clockInfo;
             updateFlags();
             return this;
@@ -793,12 +792,16 @@
         /**
          * Set time of clock in seconds.
          *
+         * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+         *
+         * <p>The value is not encoded as in GPS ICD200 documentation.
+         *
          * @param timeOfClock time of clock (seconds)
          * @return builder object
          */
         @NonNull
-        public Builder setTimeOfClock(@IntRange(from = 0, to = 604784) int timeOfClock) {
-            Preconditions.checkArgumentInRange(timeOfClock, 0, 604784, "timeOfClock");
+        public Builder setTimeOfClock(@IntRange(from = 0) long timeOfClock) {
+            Preconditions.checkArgumentNonnegative(timeOfClock);
             mTimeOfClock = timeOfClock;
             mFlags = (byte) (mFlags | HAS_TIME_OF_CLOCK);
             return this;
@@ -807,12 +810,16 @@
         /**
          * Set time of ephemeris in seconds.
          *
+         * <p>The value is in seconds since GPS epoch, regardless of the constellation.
+         *
+         * <p>The value is not encoded as in GPS ICD200 documentation.
+         *
          * @param timeOfEphemeris time of ephemeris (seconds)
          * @return builder object
          */
         @NonNull
-        public Builder setTimeOfEphemeris(@IntRange(from = 0, to = 604784) int timeOfEphemeris) {
-            Preconditions.checkArgumentInRange(timeOfEphemeris, 0, 604784, "timeOfEphemeris");
+        public Builder setTimeOfEphemeris(@IntRange(from = 0) int timeOfEphemeris) {
+            Preconditions.checkArgumentNonnegative(timeOfEphemeris);
             mTimeOfEphemeris = timeOfEphemeris;
             mFlags = (byte) (mFlags | HAS_TIME_OF_EPHEMERIS);
             return this;
diff --git a/media/java/Android.bp b/media/java/Android.bp
index c7c1d54..6878f9d 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -16,7 +16,9 @@
     exclude_srcs: [
         ":framework-media-tv-tunerresourcemanager-sources-aidl",
     ],
-    visibility: ["//frameworks/base"],
+    visibility: [
+        "//frameworks/base",
+    ],
 }
 
 filegroup {
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
new file mode 100644
index 0000000..0e9c99b
--- /dev/null
+++ b/media/java/android/media/AudioDeviceVolumeManager.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.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ * AudioDeviceVolumeManager provides access to audio device volume control.
+ */
+public class AudioDeviceVolumeManager {
+
+    // define when using Log.*
+    //private static final String TAG = "AudioDeviceVolumeManager";
+    private static IAudioService sService;
+
+    private final String mPackageName;
+
+    public AudioDeviceVolumeManager(Context context) {
+        mPackageName = context.getApplicationContext().getOpPackageName();
+    }
+
+    /**
+     * @hide
+     * Interface to receive volume changes on a device that behaves in absolute volume mode.
+     * @see #setDeviceAbsoluteMultiVolumeBehavior(AudioDeviceAttributes, List, Executor,
+     *         OnAudioDeviceVolumeChangeListener)
+     * @see #setDeviceAbsoluteVolumeBehavior(AudioDeviceAttributes, VolumeInfo, Executor,
+     *         OnAudioDeviceVolumeChangeListener)
+     */
+    public interface OnAudioDeviceVolumeChangedListener {
+        /**
+         * Called the device for the given audio device has changed.
+         * @param device the audio device whose volume has changed
+         * @param vol the new volume for the device
+         */
+        void onAudioDeviceVolumeChanged(
+                @NonNull AudioDeviceAttributes device,
+                @NonNull VolumeInfo vol);
+    }
+
+    static class ListenerInfo {
+        final @NonNull OnAudioDeviceVolumeChangedListener mListener;
+        final @NonNull Executor mExecutor;
+        final @NonNull AudioDeviceAttributes mDevice;
+
+        ListenerInfo(@NonNull OnAudioDeviceVolumeChangedListener listener, @NonNull Executor exe,
+                @NonNull AudioDeviceAttributes device) {
+            mListener = listener;
+            mExecutor = exe;
+            mDevice = device;
+        }
+    }
+
+    private final Object mDeviceVolumeListenerLock = new Object();
+    /**
+     * List of listeners for volume changes, the associated device, and their associated Executor.
+     * List is lazy-initialized on first registration
+     */
+    @GuardedBy("mDeviceVolumeListenerLock")
+    private @Nullable ArrayList<ListenerInfo> mDeviceVolumeListeners;
+
+    @GuardedBy("mDeviceVolumeListenerLock")
+    private DeviceVolumeDispatcherStub mDeviceVolumeDispatcherStub;
+
+    final class DeviceVolumeDispatcherStub extends IAudioDeviceVolumeDispatcher.Stub {
+        /**
+         * Register / unregister the stub
+         * @param register true for registering, false for unregistering
+         * @param device device for which volume is monitored
+         */
+        public void register(boolean register, @NonNull AudioDeviceAttributes device,
+                @NonNull List<VolumeInfo> volumes) {
+            try {
+                getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
+                        this, mPackageName,
+                        Objects.requireNonNull(device), Objects.requireNonNull(volumes));
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        public void dispatchDeviceVolumeChanged(
+                @NonNull AudioDeviceAttributes device, @NonNull VolumeInfo vol) {
+            final ArrayList<ListenerInfo> volumeListeners;
+            synchronized (mDeviceVolumeListenerLock) {
+                volumeListeners = (ArrayList<ListenerInfo>) mDeviceVolumeListeners.clone();
+            }
+            for (ListenerInfo listenerInfo : volumeListeners) {
+                if (listenerInfo.mDevice.equals(device)) {
+                    listenerInfo.mExecutor.execute(
+                            () -> listenerInfo.mListener.onAudioDeviceVolumeChanged(device, vol));
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Configures a device to use absolute volume model, and registers a listener for receiving
+     * volume updates to apply on that device
+     * @param device the audio device set to absolute volume mode
+     * @param volume the type of volume this device responds to
+     * @param executor the Executor used for receiving volume updates through the listener
+     * @param vclistener the callback for volume updates
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    public void setDeviceAbsoluteVolumeBehavior(
+            @NonNull AudioDeviceAttributes device,
+            @NonNull VolumeInfo volume,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+        final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
+        volumes.add(volume);
+        setDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener);
+    }
+
+    /**
+     * @hide
+     * Configures a device to use absolute volume model applied to different volume types, and
+     * registers a listener for receiving volume updates to apply on that device
+     * @param device the audio device set to absolute multi-volume mode
+     * @param volumes the list of volumes the given device responds to
+     * @param executor the Executor used for receiving volume updates through the listener
+     * @param vclistener the callback for volume updates
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    public void setDeviceAbsoluteMultiVolumeBehavior(
+            @NonNull AudioDeviceAttributes device,
+            @NonNull List<VolumeInfo> volumes,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+        Objects.requireNonNull(device);
+        Objects.requireNonNull(volumes);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(vclistener);
+
+        // TODO verify not already registered
+        //final ListenerInfo listenerInfo = new ListenerInfo(vclistener, executor, device);
+        synchronized (mDeviceVolumeListenerLock) {
+            if (mDeviceVolumeListeners == null) {
+                mDeviceVolumeListeners = new ArrayList<>();
+            }
+            if (mDeviceVolumeListeners.size() == 0) {
+                if (mDeviceVolumeDispatcherStub == null) {
+                    mDeviceVolumeDispatcherStub = new DeviceVolumeDispatcherStub();
+                }
+            }
+            mDeviceVolumeDispatcherStub.register(true, device, volumes);
+        }
+    }
+
+    private static IAudioService getService() {
+        if (sService != null) {
+            return sService;
+        }
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        sService = IAudioService.Stub.asInterface(b);
+        return sService;
+    }
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3887372..2f95928 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7294,8 +7294,13 @@
      * Ultrasound playback and capture, false otherwise.
      */
     @SystemApi
-    public static boolean isUltrasoundSupported() {
-        return AudioSystem.isUltrasoundSupported();
+    @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+    public boolean isUltrasoundSupported() {
+        try {
+            return getService().isUltrasoundSupported();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -8443,6 +8448,22 @@
         }
     }
 
+    /**
+     * Returns the audio HAL version in the form MAJOR.MINOR. If there is no audio HAL found, null
+     * will be returned.
+     *
+     * @hide
+     */
+    @TestApi
+    public static @Nullable String getHalVersion() {
+        try {
+            return getService().getHalVersion();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error querying getHalVersion", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private final Object mMuteAwaitConnectionListenerLock = new Object();
 
     @GuardedBy("mMuteAwaitConnectionListenerLock")
diff --git a/core/java/android/window/IOnFpsCallbackListener.aidl b/media/java/android/media/IAudioDeviceVolumeDispatcher.aidl
similarity index 65%
copy from core/java/android/window/IOnFpsCallbackListener.aidl
copy to media/java/android/media/IAudioDeviceVolumeDispatcher.aidl
index 3091df3..65633fe 100644
--- a/core/java/android/window/IOnFpsCallbackListener.aidl
+++ b/media/java/android/media/IAudioDeviceVolumeDispatcher.aidl
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-package android.window;
+package android.media;
+
+import android.media.AudioDeviceAttributes;
+import android.media.VolumeInfo;
 
 /**
- * @hide
+ * AIDL for the AudioService to signal audio device volume changes.
+ *
+ * {@hide}
  */
-oneway interface IOnFpsCallbackListener {
+oneway interface IAudioDeviceVolumeDispatcher {
 
-    /**
-     * Reports the fps from the registered task
-     * @param fps The frame rate per second of the task that has the registered task id
-     *            and its children.
-     */
-    void onFpsReported(in float fps);
+    void dispatchDeviceVolumeChanged(in AudioDeviceAttributes device, in VolumeInfo vol);
+
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index a6fdf6c..4fdfcdd 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -26,6 +26,7 @@
 import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.BluetoothProfileConnectionInfo;
+import android.media.IAudioDeviceVolumeDispatcher;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioModeDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -44,6 +45,7 @@
 import android.media.IVolumeController;
 import android.media.IVolumeController;
 import android.media.PlayerBase;
+import android.media.VolumeInfo;
 import android.media.VolumePolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.AudioProductStrategy;
@@ -136,6 +138,8 @@
 
     boolean isMicrophoneMuted();
 
+    boolean isUltrasoundSupported();
+
     void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
 
     oneway void setMicrophoneMuteFromSwitch(boolean on);
@@ -456,6 +460,8 @@
 
     boolean isVolumeFixed();
 
+    VolumeInfo getDefaultVolumeInfo();
+
     boolean isPstnCallAudioInterceptable();
 
     oneway void muteAwaitConnection(in int[] usagesToMute, in AudioDeviceAttributes dev,
@@ -488,4 +494,12 @@
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
     int[] getActiveAssistantServiceUids();
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+    void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
+            in IAudioDeviceVolumeDispatcher cb,
+            in String packageName,
+            in AudioDeviceAttributes device, in List<VolumeInfo> volumes);
+
+    String getHalVersion();
 }
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index e0f04a1..9f52bf1 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -108,7 +108,6 @@
     private long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
     private @HardwareBuffer.Format int mHardwareBufferFormat;
     private @NamedDataSpace int mDataSpace;
-    private boolean mUseLegacyImageFormat;
 
     // Field below is used by native code, do not access or modify.
     private int mWriterFormat;
@@ -257,7 +256,6 @@
                 + ", maxImages: " + maxImages);
         }
 
-        mUseLegacyImageFormat = useLegacyImageFormat;
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
         mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
@@ -334,12 +332,21 @@
             int hardwareBufferFormat, int dataSpace, int width, int height, long usage) {
         mMaxImages = maxImages;
         mUsage = usage;
-        mHardwareBufferFormat = hardwareBufferFormat;
-        mDataSpace = dataSpace;
-        int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+        int imageFormat;
+        // if useSurfaceImageFormatInfo is true, imageFormat will be set to UNKNOWN
+        // and retrieve corresponding hardwareBufferFormat and dataSpace here.
+        if (useSurfaceImageFormatInfo) {
+            imageFormat = ImageFormat.UNKNOWN;
+            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+            mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+        } else {
+            imageFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mDataSpace = dataSpace;
+        }
 
         initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
-                publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+                imageFormat, hardwareBufferFormat, dataSpace, width, height, usage);
     }
 
     /**
@@ -884,27 +891,17 @@
         private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
         private @NamedDataSpace int mDataSpace = DataSpace.DATASPACE_UNKNOWN;
         private boolean mUseSurfaceImageFormatInfo = true;
-        // set this as true temporarily now as a workaround to get correct format
-        // when using surface format by default without overriding the image format
-        // in the builder pattern
-        private boolean mUseLegacyImageFormat = true;
+        private boolean mUseLegacyImageFormat = false;
 
         /**
          * Constructs a new builder for {@link ImageWriter}.
          *
-         * <p>Uses {@code surface} input parameter to retrieve image format, hal format
-         * and hal dataspace value for default. </p>
-         *
          * @param surface The destination Surface this writer produces Image data into.
          *
          * @throws IllegalArgumentException if the surface is already abandoned.
          */
         public Builder(@NonNull Surface surface) {
             mSurface = surface;
-            // retrieve format from surface
-            mImageFormat = SurfaceUtils.getSurfaceFormat(surface);
-            mDataSpace = SurfaceUtils.getSurfaceDataspace(surface);
-            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mImageFormat);
         }
 
         /**
@@ -1058,11 +1055,6 @@
             mWidth = writer.mWidth;
             mHeight = writer.mHeight;
             mDataSpace = writer.mDataSpace;
-
-            if (!mOwner.mUseLegacyImageFormat) {
-                mFormat = PublicFormatUtils.getPublicFormat(
-                    mOwner.mHardwareBufferFormat, mDataSpace);
-            }
         }
 
         @Override
@@ -1083,7 +1075,7 @@
         public int getFormat() {
             throwISEIfImageIsInvalid();
 
-            if (mOwner.mUseLegacyImageFormat && mFormat == -1) {
+            if (mFormat == -1) {
                 mFormat = nativeGetFormat(mDataSpace);
             }
             return mFormat;
diff --git a/media/java/android/media/VolumeInfo.aidl b/media/java/android/media/VolumeInfo.aidl
new file mode 100644
index 0000000..aa82f52
--- /dev/null
+++ b/media/java/android/media/VolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable VolumeInfo;
diff --git a/media/java/android/media/VolumeInfo.java b/media/java/android/media/VolumeInfo.java
new file mode 100644
index 0000000..c61b0e5
--- /dev/null
+++ b/media/java/android/media/VolumeInfo.java
@@ -0,0 +1,380 @@
+/*
+ * 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @hide
+ * A class to represent type of volume information.
+ * Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
+ * Volume index is optional when used to represent a category of volume.
+ * Index ranges are supported too, making the representation of volume changes agnostic to the
+ * range (e.g. can be used to map BT A2DP absolute volume range to internal range).
+ *
+ * Note: this class is not yet part of the SystemApi but is intended to be gradually introduced
+ *       particularly in parts of the audio framework that suffer from code ambiguity when
+ *       dealing with different volume ranges / units.
+ */
+public final class VolumeInfo implements Parcelable {
+    private static final String TAG = "VolumeInfo";
+
+    private final boolean mUsesStreamType; // false implies AudioVolumeGroup is used
+    private final boolean mIsMuted;
+    private final int mVolIndex;
+    private final int mMinVolIndex;
+    private final int mMaxVolIndex;
+    private final int mVolGroupId;
+    private final int mStreamType;
+
+    private static IAudioService sService;
+    private static VolumeInfo sDefaultVolumeInfo;
+
+    private VolumeInfo(boolean usesStreamType, boolean isMuted, int volIndex,
+            int minVolIndex, int maxVolIndex,
+            int volGroupId, int streamType) {
+        mUsesStreamType = usesStreamType;
+        mIsMuted = isMuted;
+        mVolIndex = volIndex;
+        mMinVolIndex = minVolIndex;
+        mMaxVolIndex = maxVolIndex;
+        mVolGroupId = volGroupId;
+        mStreamType = streamType;
+    }
+
+    /**
+     * Indicates whether this instance has a stream type associated to it.
+     * Note this method returning true implies {@link #hasVolumeGroup()} returns false.
+     * (e.g. {@link AudioManager#STREAM_MUSIC}).
+     * @return true if it has stream type information
+     */
+    public boolean hasStreamType() {
+        return mUsesStreamType;
+    }
+
+    /**
+     * Returns the associated stream type, or will throw if {@link #hasStreamType()} returned false.
+     * @return a stream type value, see AudioManager.STREAM_*
+     */
+    public int getStreamType() {
+        if (!mUsesStreamType) {
+            throw new IllegalStateException("VolumeInfo doesn't use stream types");
+        }
+        return mStreamType;
+    }
+
+    /**
+     * Indicates whether this instance has a {@link AudioVolumeGroup} associated to it.
+     * Note this method returning true implies {@link #hasStreamType()} returns false.
+     * @return true if it has volume group information
+     */
+    public boolean hasVolumeGroup() {
+        return !mUsesStreamType;
+    }
+
+    /**
+     * Returns the associated volume group, or will throw if {@link #hasVolumeGroup()} returned
+     * false.
+     * @return the volume group corresponding to this VolumeInfo, or null if an error occurred
+     * in the volume group management
+     */
+    public @Nullable AudioVolumeGroup getVolumeGroup() {
+        if (mUsesStreamType) {
+            throw new IllegalStateException("VolumeInfo doesn't use AudioVolumeGroup");
+        }
+        List<AudioVolumeGroup> volGroups = AudioVolumeGroup.getAudioVolumeGroups();
+        for (AudioVolumeGroup group : volGroups) {
+            if (group.getId() == mVolGroupId) {
+                return group;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns whether this instance is conveying a mute state.
+     * @return true if the volume state is muted
+     */
+    public boolean isMuted() {
+        return mIsMuted;
+    }
+
+    /**
+     * A value used to express no volume index has been set.
+     */
+    public static final int INDEX_NOT_SET = -100;
+
+    /**
+     * Returns the volume index.
+     * @return a volume index, or {@link #INDEX_NOT_SET} if no index was set, in which case this
+     *      instance is used to express a volume representation type (stream vs group) and
+     *      optionally its volume range
+     */
+    public int getVolumeIndex() {
+        return mVolIndex;
+    }
+
+    /**
+     * Returns the minimum volume index.
+     * @return the minimum volume index, or {@link #INDEX_NOT_SET} if no minimum index was set.
+     */
+    public int getMinVolumeIndex() {
+        return mMinVolIndex;
+    }
+
+    /**
+     * Returns the maximum volume index.
+     * @return the maximum volume index, or {@link #INDEX_NOT_SET} if no maximum index was
+     *      set.
+     */
+    public int getMaxVolumeIndex() {
+        return mMaxVolIndex;
+    }
+
+    /**
+     * Returns the default info for the platform, typically initialized
+     * to STREAM_MUSIC with min/max initialized to the associated range
+     * @return the default VolumeInfo for the device
+     */
+    public static @NonNull VolumeInfo getDefaultVolumeInfo() {
+        if (sService == null) {
+            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+            sService = IAudioService.Stub.asInterface(b);
+        }
+        if (sDefaultVolumeInfo == null) {
+            try {
+                sDefaultVolumeInfo = sService.getDefaultVolumeInfo();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling getDefaultVolumeInfo", e);
+                // return a valid value, but don't cache it
+                return new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build();
+            }
+        }
+        return sDefaultVolumeInfo;
+    }
+
+    /**
+     * The builder class for creating and initializing, or copying and modifying VolumeInfo
+     * instances
+     */
+    public static final class Builder {
+        private boolean mUsesStreamType = true; // false implies AudioVolumeGroup is used
+        private int mStreamType = AudioManager.STREAM_MUSIC;
+        private boolean mIsMuted = false;
+        private int mVolIndex = INDEX_NOT_SET;
+        private int mMinVolIndex = INDEX_NOT_SET;
+        private int mMaxVolIndex = INDEX_NOT_SET;
+        private int mVolGroupId = -Integer.MIN_VALUE;
+
+        /**
+         * Builder constructor for stream type-based VolumeInfo
+         */
+        public Builder(int streamType) {
+            // TODO validate stream type
+            mUsesStreamType = true;
+            mStreamType = streamType;
+        }
+
+        /**
+         * Builder constructor for volume group-based VolumeInfo
+         */
+        public Builder(@NonNull AudioVolumeGroup volGroup) {
+            Objects.requireNonNull(volGroup);
+            mUsesStreamType = false;
+            mStreamType = -Integer.MIN_VALUE;
+            mVolGroupId = volGroup.getId();
+        }
+
+        /**
+         * Builder constructor to copy a given VolumeInfo.
+         * Note you can't change the stream type or volume group later.
+         */
+        public Builder(@NonNull VolumeInfo info) {
+            Objects.requireNonNull(info);
+            mUsesStreamType = info.mUsesStreamType;
+            mStreamType = info.mStreamType;
+            mIsMuted = info.mIsMuted;
+            mVolIndex = info.mVolIndex;
+            mMinVolIndex = info.mMinVolIndex;
+            mMaxVolIndex = info.mMaxVolIndex;
+            mVolGroupId = info.mVolGroupId;
+        }
+
+        /**
+         * Sets whether the volume is in a muted state
+         * @param isMuted
+         * @return the same builder instance
+         */
+        public @NonNull Builder setMuted(boolean isMuted) {
+            mIsMuted = isMuted;
+            return this;
+        }
+
+        /**
+         * Sets the volume index
+         * @param volIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
+         * @return the same builder instance
+         */
+        // TODO should we allow muted true + volume index set? (useful when toggling mute on/off?)
+        public @NonNull Builder setVolumeIndex(int volIndex) {
+            if (volIndex != INDEX_NOT_SET && volIndex < 0) {
+                throw new IllegalArgumentException("Volume index cannot be negative");
+            }
+            mVolIndex = volIndex;
+            return this;
+        }
+
+        /**
+         * Sets the minimum volume index
+         * @param minIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
+         * @return the same builder instance
+         */
+        public @NonNull Builder setMinVolumeIndex(int minIndex) {
+            if (minIndex != INDEX_NOT_SET && minIndex < 0) {
+                throw new IllegalArgumentException("Min volume index cannot be negative");
+            }
+            mMinVolIndex = minIndex;
+            return this;
+        }
+
+        /**
+         * Sets the maximum volume index
+         * @param maxIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
+         * @return the same builder instance
+         */
+        public @NonNull Builder setMaxVolumeIndex(int maxIndex) {
+            if (maxIndex != INDEX_NOT_SET && maxIndex < 0) {
+                throw new IllegalArgumentException("Max volume index cannot be negative");
+            }
+            mMaxVolIndex = maxIndex;
+            return this;
+        }
+
+        /**
+         * Builds the VolumeInfo with the data given to the builder
+         * @return the new VolumeInfo instance
+         */
+        public @NonNull VolumeInfo build() {
+            if (mVolIndex != INDEX_NOT_SET) {
+                if (mMinVolIndex != INDEX_NOT_SET && mVolIndex < mMinVolIndex) {
+                    throw new IllegalArgumentException("Volume index:" + mVolIndex
+                            + " lower than min index:" + mMinVolIndex);
+                }
+                if (mMaxVolIndex != INDEX_NOT_SET && mVolIndex > mMaxVolIndex) {
+                    throw new IllegalArgumentException("Volume index:" + mVolIndex
+                            + " greater than max index:" + mMaxVolIndex);
+                }
+            }
+            if (mMinVolIndex != INDEX_NOT_SET && mMaxVolIndex != INDEX_NOT_SET
+                    && mMinVolIndex > mMaxVolIndex) {
+                throw new IllegalArgumentException("Min volume index:" + mMinVolIndex
+                        + " greater than max index:" + mMaxVolIndex);
+            }
+            return new VolumeInfo(mUsesStreamType, mIsMuted,
+                    mVolIndex, mMinVolIndex, mMaxVolIndex,
+                    mVolGroupId, mStreamType);
+        }
+    }
+
+    //-----------------------------------------------
+    // Parcelable
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUsesStreamType, mStreamType, mIsMuted,
+                mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroupId);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        VolumeInfo that = (VolumeInfo) o;
+        return ((mUsesStreamType == that.mUsesStreamType)
+                && (mStreamType == that.mStreamType)
+            && (mIsMuted == that.mIsMuted)
+            && (mVolIndex == that.mVolIndex)
+            && (mMinVolIndex == that.mMinVolIndex)
+            && (mMaxVolIndex == that.mMaxVolIndex)
+            && (mVolGroupId == that.mVolGroupId));
+    }
+
+    @Override
+    public String toString() {
+        return new String("VolumeInfo:"
+                + (mUsesStreamType ? (" streamType:" + mStreamType)
+                    : (" volGroupId" + mVolGroupId))
+                + " muted:" + mIsMuted
+                + ((mVolIndex != INDEX_NOT_SET) ? (" volIndex:" + mVolIndex) : "")
+                + ((mMinVolIndex != INDEX_NOT_SET) ? (" min:" + mMinVolIndex) : "")
+                + ((mMaxVolIndex != INDEX_NOT_SET) ? (" max:" + mMaxVolIndex) : ""));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeBoolean(mUsesStreamType);
+        dest.writeInt(mStreamType);
+        dest.writeBoolean(mIsMuted);
+        dest.writeInt(mVolIndex);
+        dest.writeInt(mMinVolIndex);
+        dest.writeInt(mMaxVolIndex);
+        dest.writeInt(mVolGroupId);
+    }
+
+    private VolumeInfo(@NonNull Parcel in) {
+        mUsesStreamType = in.readBoolean();
+        mStreamType = in.readInt();
+        mIsMuted = in.readBoolean();
+        mVolIndex = in.readInt();
+        mMinVolIndex = in.readInt();
+        mMaxVolIndex = in.readInt();
+        mVolGroupId = in.readInt();
+    }
+
+    public static final @NonNull Parcelable.Creator<VolumeInfo> CREATOR =
+            new Parcelable.Creator<VolumeInfo>() {
+                /**
+                 * Rebuilds a VolumeInfo previously stored with writeToParcel().
+                 * @param p Parcel object to read the VolumeInfo from
+                 * @return a new VolumeInfo created from the data in the parcel
+                 */
+                public VolumeInfo createFromParcel(Parcel p) {
+                    return new VolumeInfo(p);
+                }
+
+                public VolumeInfo[] newArray(int size) {
+                    return new VolumeInfo[size];
+                }
+            };
+}
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 5348d4e..74c5499 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -112,20 +112,11 @@
     // Binder stub for receiving device notifications from MidiService
     private class DeviceListener extends IMidiDeviceListener.Stub {
         private final DeviceCallback mCallback;
-        private final Handler mHandler;
         private final Executor mExecutor;
         private final int mTransport;
 
-        DeviceListener(DeviceCallback callback, Handler handler, int transport) {
-            mCallback = callback;
-            mHandler = handler;
-            mExecutor = null;
-            mTransport = transport;
-        }
-
         DeviceListener(DeviceCallback callback, Executor executor, int transport) {
             mCallback = callback;
-            mHandler = null;
             mExecutor = executor;
             mTransport = transport;
         }
@@ -136,13 +127,6 @@
                 if (mExecutor != null) {
                     mExecutor.execute(() ->
                             mCallback.onDeviceAdded(device));
-                } else if (mHandler != null) {
-                    final MidiDeviceInfo deviceF = device;
-                    mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                mCallback.onDeviceAdded(deviceF);
-                            }
-                        });
                 } else {
                     mCallback.onDeviceAdded(device);
                 }
@@ -155,13 +139,6 @@
                 if (mExecutor != null) {
                     mExecutor.execute(() ->
                             mCallback.onDeviceRemoved(device));
-                } else if (mHandler != null) {
-                    final MidiDeviceInfo deviceF = device;
-                    mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                mCallback.onDeviceRemoved(deviceF);
-                            }
-                        });
                 } else {
                     mCallback.onDeviceRemoved(device);
                 }
@@ -173,13 +150,6 @@
             if (mExecutor != null) {
                 mExecutor.execute(() ->
                         mCallback.onDeviceStatusChanged(status));
-            } else if (mHandler != null) {
-                final MidiDeviceStatus statusF = status;
-                mHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mCallback.onDeviceStatusChanged(statusF);
-                        }
-                    });
             } else {
                 mCallback.onDeviceStatusChanged(status);
             }
@@ -275,7 +245,11 @@
      */
     @Deprecated
     public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
-        DeviceListener deviceListener = new DeviceListener(callback, handler,
+        Executor executor = null;
+        if (handler != null) {
+            executor = handler::post;
+        }
+        DeviceListener deviceListener = new DeviceListener(callback, executor,
                 TRANSPORT_MIDI_BYTE_STREAM);
         try {
             mService.registerListener(mToken, deviceListener);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 96809bd..69fe5ee 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1863,12 +1863,9 @@
      * Returns a priority for the given use case type and the client's foreground or background
      * status.
      *
-     * @param useCase the use case type of the client. When the given use case type is invalid,
-     *        the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}.
-     * @param sessionId the unique id of the session owned by the client. When {@code null},
-     *        the caller will be used as a client. When the session is invalid, background status
-     *        will be used as a client's status. Otherwise, TV app corresponding to the given
-     *        session id will be used as a client.
+     * @param useCase the use case type of the client.
+     *        {@see TvInputService#PriorityHintUseCaseType}.
+     * @param sessionId the unique id of the session owned by the client.
      *        {@see TvInputService#onCreateSession(String, String)}.
      *
      * @return the use case priority value for the given use case type and the client's foreground
@@ -1879,11 +1876,35 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
     public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase,
-            @Nullable String sessionId) {
+            @NonNull String sessionId) {
+        Preconditions.checkNotNull(sessionId);
+        if (!isValidUseCase(useCase)) {
+            throw new IllegalArgumentException("Invalid use case: " + useCase);
+        }
         return getClientPriorityInternal(useCase, sessionId);
     };
 
     /**
+     * Returns a priority for the given use case type and the caller's foreground or background
+     * status.
+     *
+     * @param useCase the use case type of the caller.
+     *        {@see TvInputService#PriorityHintUseCaseType}.
+     *
+     * @return the use case priority value for the given use case type and the caller's foreground
+     *         or background status.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase) {
+        if (!isValidUseCase(useCase)) {
+            throw new IllegalArgumentException("Invalid use case: " + useCase);
+        }
+        return getClientPriorityInternal(useCase, null);
+    };
+    /**
      * Creates a recording {@link Session} for a given TV input.
      *
      * <p>The number of sessions that can be created at the same time is limited by the capability
@@ -1935,6 +1956,14 @@
         }
     }
 
+    private boolean isValidUseCase(int useCase) {
+        return useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
+            || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN
+            || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
+            || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE
+            || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
+    }
+
     /**
      * Returns the TvStreamConfig list of the given TV input.
      *
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ed1ac0..397c704 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1839,7 +1839,7 @@
     if (mDemuxClient == nullptr) {
         return nullptr;
     }
-    long time = mDemuxClient->getAvSyncTime((int)id);
+    int64_t time = mDemuxClient->getAvSyncTime((int)id);
     if (time >= 0) {
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         jclass longClazz = env->FindClass("java/lang/Long");
@@ -4592,7 +4592,8 @@
         ALOGD("Failed to GetByteArrayElements");
         return -1;
     }
-    long realSize = dvrClient->readFromBuffer(reinterpret_cast<signed char *>(src) + offset, size);
+    int64_t realSize =
+            dvrClient->readFromBuffer(reinterpret_cast<signed char *>(src) + offset, size);
     env->ReleaseByteArrayElements(buffer, src, 0);
     return (jlong)realSize;
 }
@@ -4624,7 +4625,8 @@
         return -1;
     }
 
-    long realSize = dvrClient->writeToBuffer(reinterpret_cast<signed char *>(dst) + offset, size);
+    int64_t realSize =
+            dvrClient->writeToBuffer(reinterpret_cast<signed char *>(dst) + offset, size);
     env->ReleaseByteArrayElements(buffer, dst, 0);
     return (jlong)realSize;
 }
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 9039011..858f85f 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,7 +20,7 @@
         xmlns:tools="http://schemas.android.com/tools"
         package="com.android.bluetoothmidiservice"
         >
-    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
 
     <uses-feature android:name="android.hardware.bluetooth_le"
          android:required="true"/>
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index 5a900c7..99dcaaa 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.bluetoothmidiservice"
           >
-    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
     <application
         android:label="BluetoothMidi"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index e22580d..21c5029 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -40,10 +40,9 @@
     return AChoreographer_routePostFrameCallbackDelayed64(choreographer, callback, data,
                                                           delayMillis);
 }
-void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
-                                              AChoreographer_extendedFrameCallback callback,
-                                              void* data) {
-    return AChoreographer_routePostExtendedFrameCallback(choreographer, callback, data);
+void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
+                                      AChoreographer_vsyncCallback callback, void* data) {
+    return AChoreographer_routePostVsyncCallback(choreographer, callback, data);
 }
 void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
                                                 AChoreographer_refreshRateCallback callback,
@@ -71,10 +70,10 @@
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(data,
-                                                                                         index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos(
+            data, index);
 }
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
diff --git a/native/android/input.cpp b/native/android/input.cpp
index c06c81e..a231d8f 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -283,6 +283,21 @@
             axis, pointer_index, history_index);
 }
 
+int32_t AMotionEvent_getActionButton(const AInputEvent* motion_event) {
+    return static_cast<const MotionEvent*>(motion_event)->getActionButton();
+}
+
+int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) {
+    switch (static_cast<const MotionEvent*>(motion_event)->getClassification()) {
+        case android::MotionClassification::NONE:
+            return AMOTION_EVENT_CLASSIFICATION_NONE;
+        case android::MotionClassification::AMBIGUOUS_GESTURE:
+            return AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE;
+        case android::MotionClassification::DEEP_PRESS:
+            return AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS;
+    }
+}
+
 const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) {
     MotionEvent* eventSrc = android::android_view_MotionEvent_getNativePtr(env, motionEvent);
     if (eventSrc == nullptr) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3009a36..4aa5bbf 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -29,12 +29,12 @@
     AChoreographer_postFrameCallbackDelayed64; # introduced=29
     AChoreographer_registerRefreshRateCallback; # introduced=30
     AChoreographer_unregisterRefreshRateCallback; # introduced=30
-    AChoreographer_postExtendedFrameCallback;  # introduced=33
+    AChoreographer_postVsyncCallback;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimeNanos;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelinesLength;  # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos;  # introduced=33
     AConfiguration_copy;
     AConfiguration_delete;
@@ -114,8 +114,10 @@
     ALooper_removeFd;
     ALooper_wake;
     AMotionEvent_getAction;
+    AMotionEvent_getActionButton; # introduced=Tiramisu
     AMotionEvent_getAxisValue; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     AMotionEvent_getButtonState; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    AMotionEvent_getClassification; # introduced=Tiramisu
     AMotionEvent_getDownTime;
     AMotionEvent_getEdgeFlags;
     AMotionEvent_getEventTime;
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 0e60873..9f5bfd4 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -34,11 +34,13 @@
 android_app {
     name: "CompanionDeviceManager",
     defaults: ["platform_app_defaults"],
+    certificate: "platform",
     srcs: ["src/**/*.java"],
 
     static_libs: [
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-extensions",
+        "androidx.recyclerview_recyclerview",
         "androidx.appcompat_appcompat",
     ],
 
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 06f2d9d..8b5d214 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -31,6 +31,7 @@
     <uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
 
     <application
         android:allowClearUserData="true"
diff --git a/core/res/res/drawable/default_dream_preview.xml b/packages/CompanionDeviceManager/res/color/selector.xml
similarity index 72%
rename from core/res/res/drawable/default_dream_preview.xml
rename to packages/CompanionDeviceManager/res/color/selector.xml
index bf4a04b..fda827d 100644
--- a/core/res/res/drawable/default_dream_preview.xml
+++ b/packages/CompanionDeviceManager/res/color/selector.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2022 The Android Open Source Project
   ~
@@ -15,6 +14,7 @@
   ~ limitations under the License.
   -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="@android:color/white"/>
-</shape>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@android:color/darker_gray"/> <!-- pressed -->
+    <item android:color="@android:color/white"/>
+</selector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 313e164..70cbfdf 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -49,8 +49,8 @@
             android:layout_height="0dp"
             android:layout_weight="1">
 
-        <ListView
-                android:id="@+id/device_list"
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/device_list"
                 style="@android:style/Widget.Material.ListView"
                 android:layout_width="match_parent"
                 android:layout_height="200dp" />
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index d79aea6..153fc1f 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -19,7 +19,8 @@
               android:layout_height="wrap_content"
               android:orientation="horizontal"
               android:gravity="center_vertical"
-              android:padding="12dp">
+              android:padding="12dp"
+              android:background="@color/selector">
 
     <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 9d3fc7f..a6a8fcf 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -37,6 +37,7 @@
 import android.companion.CompanionDeviceManager;
 import android.companion.IAssociationRequestCallback;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.MacAddress;
 import android.os.Bundle;
 import android.os.Handler;
@@ -46,10 +47,13 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
-import android.widget.ListView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
 
 /**
  *  A CompanionDevice activity response for showing the available
@@ -68,6 +72,9 @@
     private static final String EXTRA_ASSOCIATION_REQUEST = "association_request";
     private static final String EXTRA_RESULT_RECEIVER = "result_receiver";
 
+    // Activity result: Internal Error.
+    private static final int RESULT_INTERNAL_ERROR = 2;
+
     // AssociationRequestsProcessor -> UI
     private static final int RESULT_CODE_ASSOCIATION_CREATED = 0;
     private static final String EXTRA_ASSOCIATION = "association";
@@ -92,9 +99,9 @@
     // regular.
     private Button mButtonAllow;
 
-    // The list is only shown for multiple-device regular association request, after at least one
-    // matching device is found.
-    private @Nullable ListView mListView;
+    // The recycler view is only shown for multiple-device regular association request, after
+    // at least one matching device is found.
+    private @Nullable RecyclerView mRecyclerView;
     private @Nullable DeviceListAdapter mAdapter;
 
     // The flag used to prevent double taps, that may lead to sending several requests for creating
@@ -188,26 +195,36 @@
     private void initUI() {
         if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest);
 
+        final String packageName = mRequest.getPackageName();
+        final int userId = mRequest.getUserId();
+        final CharSequence appLabel;
+
+        try {
+            appLabel = getApplicationLabel(this, packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Package u" + userId + "/" + packageName + " not found.");
+
+            CompanionDeviceDiscoveryService.stop(this);
+            setResultAndFinish(null, RESULT_INTERNAL_ERROR);
+            return;
+        }
+
         setContentView(R.layout.activity_confirmation);
 
         mTitle = findViewById(R.id.title);
         mSummary = findViewById(R.id.summary);
 
-        mListView = findViewById(R.id.device_list);
-        mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position));
+        mRecyclerView = findViewById(R.id.device_list);
+        mAdapter = new DeviceListAdapter(this, this::onListItemClick);
 
         mButtonAllow = findViewById(R.id.btn_positive);
         mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
         findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick);
 
-        final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName());
         if (mRequest.isSelfManaged()) {
             initUiForSelfManagedAssociation(appLabel);
         } else if (mRequest.isSingleDevice()) {
-            // TODO(b/211722613)
-            // Treat singleDevice as the multipleDevices for now
-            // initUiForSingleDevice(appLabel);
-            initUiForMultipleDevices(appLabel);
+            initUiForSingleDevice(appLabel);
         } else {
             initUiForMultipleDevices(appLabel);
         }
@@ -221,12 +238,6 @@
     }
 
     private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) {
-        if (mSelectedDevice != null) {
-            if (DEBUG) Log.w(TAG, "Already selected.");
-            return;
-        }
-        mSelectedDevice = requireNonNull(selectedDevice);
-
         final MacAddress macAddress = selectedDevice.getMacAddress();
         onAssociationApproved(macAddress);
     }
@@ -262,7 +273,7 @@
         if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association);
 
         // Don't need to notify the app, CdmService has already done that. Just finish.
-        setResultAndFinish(association);
+        setResultAndFinish(association, RESULT_OK);
     }
 
     private void cancel(boolean discoveryTimeout) {
@@ -289,10 +300,10 @@
         }
 
         // ... then set result and finish ("sending" onActivityResult()).
-        setResultAndFinish(null);
+        setResultAndFinish(null, RESULT_CANCELED);
     }
 
-    private void setResultAndFinish(@Nullable AssociationInfo association) {
+    private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
         if (DEBUG) Log.i(TAG, "setResultAndFinish(), association=" + association);
 
         final Intent data = new Intent();
@@ -302,7 +313,7 @@
                 data.putExtra(CompanionDeviceManager.EXTRA_DEVICE, mSelectedDevice.getDevice());
             }
         }
-        setResult(association != null ? RESULT_OK : RESULT_CANCELED, data);
+        setResult(resultCode, data);
 
         finish();
     }
@@ -340,19 +351,32 @@
         mTitle.setText(title);
         mSummary.setText(summary);
 
-        mListView.setVisibility(View.GONE);
+        mRecyclerView.setVisibility(View.GONE);
     }
 
     private void initUiForSingleDevice(CharSequence appLabel) {
         if (DEBUG) Log.i(TAG, "initUiFor_SingleDevice()");
 
-        // TODO: use real name
-        final String deviceName = "<device>";
         final String deviceProfile = mRequest.getDeviceProfile();
 
+        CompanionDeviceDiscoveryService.getScanResult().observe(this,
+                deviceFilterPairs -> updateSingleDeviceUi(
+                        deviceFilterPairs, deviceProfile, appLabel));
+
+        mRecyclerView.setVisibility(View.GONE);
+    }
+
+    private void updateSingleDeviceUi(List<DeviceFilterPair<?>> deviceFilterPairs,
+            String deviceProfile, CharSequence appLabel) {
+        // Ignore "empty" scan reports.
+        if (deviceFilterPairs.isEmpty()) return;
+        mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
+
+        final String deviceName = mSelectedDevice.getDisplayName();
         final Spanned title = getHtmlFromResources(
                 this, R.string.confirmation_title, appLabel, deviceName);
         final Spanned summary;
+
         if (deviceProfile == null) {
             summary = getHtmlFromResources(this, R.string.summary_generic);
         } else if (deviceProfile.equals(DEVICE_PROFILE_WATCH)) {
@@ -363,8 +387,6 @@
 
         mTitle.setText(title);
         mSummary.setText(summary);
-
-        mListView.setVisibility(View.GONE);
     }
 
     private void initUiForMultipleDevices(CharSequence appLabel) {
@@ -389,10 +411,12 @@
         mTitle.setText(title);
         mSummary.setText(summary);
 
-        mAdapter = new DeviceListAdapter(this);
+        mAdapter = new DeviceListAdapter(this, this::onListItemClick);
 
         // TODO: hide the list and show a spinner until a first device matching device is found.
-        mListView.setAdapter(mAdapter);
+        mRecyclerView.setAdapter(mAdapter);
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
         CompanionDeviceDiscoveryService.getScanResult().observe(
                 /* lifecycleOwner */ this,
                 /* observer */ mAdapter);
@@ -405,6 +429,16 @@
         if (DEBUG) Log.d(TAG, "onListItemClick() " + position);
 
         final DeviceFilterPair<?> selectedDevice = mAdapter.getItem(position);
+
+        if (mSelectedDevice != null) {
+            if (DEBUG) Log.w(TAG, "Already selected.");
+            return;
+        }
+        // Notify the adapter to highlight the selected item.
+        mAdapter.setSelectedPosition(position);
+
+        mSelectedDevice = requireNonNull(selectedDevice);
+
         onUserSelectedDevice(selectedDevice);
     }
 
@@ -417,9 +451,7 @@
         if (mRequest.isSelfManaged()) {
             onAssociationApproved(null);
         } else {
-            // TODO(b/211722613): call onUserSelectedDevice().
-            throw new UnsupportedOperationException(
-                    "isSingleDevice() requests are not supported yet.");
+            onUserSelectedDevice(mSelectedDevice);
         }
     }
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 5d48708..5f07fcf 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -103,6 +103,8 @@
 
     private final Runnable mTimeoutRunnable = this::timeout;
 
+    private boolean mStopAfterFirstMatch;;
+
     /**
      * A state enum for devices' discovery.
      */
@@ -163,8 +165,7 @@
                 break;
 
             case ACTION_STOP_DISCOVERY:
-                stopDiscoveryAndFinish();
-                sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
+                stopDiscoveryAndFinish(/* timeout */ false);
                 break;
         }
         return START_NOT_STICKY;
@@ -182,6 +183,7 @@
         requireNonNull(request);
 
         if (mDiscoveryStarted) throw new RuntimeException("Discovery in progress.");
+        mStopAfterFirstMatch = request.isSingleDevice();
         mDiscoveryStarted = true;
         sStateLiveData.setValue(DiscoveryState.DISCOVERY_IN_PROGRESS);
         sScanResultsLiveData.setValue(Collections.emptyList());
@@ -208,7 +210,7 @@
     }
 
     @MainThread
-    private void stopDiscoveryAndFinish() {
+    private void stopDiscoveryAndFinish(boolean timeout) {
         if (DEBUG) Log.i(TAG, "stopDiscovery()");
 
         if (!mDiscoveryStarted) {
@@ -243,6 +245,12 @@
 
         Handler.getMain().removeCallbacks(mTimeoutRunnable);
 
+        if (timeout) {
+            sStateLiveData.setValue(DiscoveryState.FINISHED_TIMEOUT);
+        } else {
+            sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
+        }
+
         // "Finish".
         stopSelf();
     }
@@ -332,6 +340,7 @@
     private void onDeviceFound(@NonNull DeviceFilterPair<?> device) {
         runOnMainThread(() -> {
             if (DEBUG) Log.v(TAG, "onDeviceFound() " + device);
+            if (mDiscoveryStopped) return;
             if (mDevicesFound.contains(device)) {
                 // TODO: update the device instead of ignoring (new found device may contain
                 //  additional/updated info, eg. name of the device).
@@ -347,6 +356,10 @@
             mDevicesFound.add(device);
             // Then: notify observers.
             sScanResultsLiveData.setValue(mDevicesFound);
+            // Stop discovery when there's one device found for singleDevice.
+            if (mStopAfterFirstMatch) {
+                stopDiscoveryAndFinish(/* timeout */ false);
+            }
         });
     }
 
@@ -378,8 +391,7 @@
 
     private void timeout() {
         if (DEBUG) Log.i(TAG, "timeout()");
-        stopDiscoveryAndFinish();
-        sStateLiveData.setValue(DiscoveryState.FINISHED_TIMEOUT);
+        stopDiscoveryAndFinish(/* timeout */ true);
     }
 
     @Override
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index 198b778..e5513b0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -15,73 +15,84 @@
  */
 
 package com.android.companiondevicemanager;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.RecyclerView;
 
 import java.util.List;
-
 /**
  * Adapter for the list of "found" devices.
  */
-class DeviceListAdapter extends BaseAdapter implements Observer<List<DeviceFilterPair<?>>> {
+class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> implements
+        Observer<List<DeviceFilterPair<?>>> {
+    public int mSelectedPosition = RecyclerView.NO_POSITION;
+
     private final Context mContext;
 
     // List if pairs (display name, address)
     private List<DeviceFilterPair<?>> mDevices;
 
-    DeviceListAdapter(Context context) {
+    private OnItemClickListener mListener;
+
+    private static final int TYPE_WIFI = 0;
+    private static final int TYPE_BT = 1;
+
+    DeviceListAdapter(Context context, OnItemClickListener listener) {
         mContext = context;
+        mListener = listener;
     }
 
-    @Override
-    public int getCount() {
-        return mDevices != null ? mDevices.size() : 0;
-    }
-
-    @Override
     public DeviceFilterPair<?> getItem(int position) {
         return mDevices.get(position);
     }
 
     @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(
+                R.layout.list_item_device, parent, false);
+        ViewHolder viewHolder = new ViewHolder(view);
+        if (viewType == TYPE_WIFI) {
+            viewHolder.mImageView.setImageDrawable(getIcon(
+                    com.android.internal.R.drawable.ic_wifi_signal_3));
+        } else {
+            viewHolder.mImageView.setImageDrawable(getIcon(
+                    android.R.drawable.stat_sys_data_bluetooth));
+        }
+        return viewHolder;
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.itemView.setSelected(mSelectedPosition == position);
+        holder.mTextView.setText(mDevices.get(position).getDisplayName());
+        holder.itemView.setOnClickListener(v -> mListener.onItemClick(position));
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return isWifiDevice(position) ? TYPE_WIFI : TYPE_BT;
+    }
+
+    @Override
     public long getItemId(int position) {
         return position;
     }
 
     @Override
-    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
-        final View view = convertView != null
-                ? convertView
-                : LayoutInflater.from(mContext).inflate(R.layout.list_item_device, parent, false);
-
-        final DeviceFilterPair<?> item = getItem(position);
-        bindView(view, item);
-
-        return view;
+    public int getItemCount() {
+        return mDevices != null ? mDevices.size() : 0;
     }
 
-    private void bindView(@NonNull View view, DeviceFilterPair<?> item) {
-        final TextView textView = view.findViewById(android.R.id.text1);
-        textView.setText(item.getDisplayName());
-
-        final ImageView iconView = view.findViewById(android.R.id.icon);
-
-        // TODO(b/211417476): Set either Bluetooth or WiFi icon.
-        iconView.setVisibility(View.GONE);
-        // final int iconRes = isBt ? android.R.drawable.stat_sys_data_bluetooth
-        //        : com.android.internal.R.drawable.ic_wifi_signal_3;
-        // final Drawable icon = getTintedIcon(mResources, iconRes);
-        // iconView.setImageDrawable(icon);
+    public void setSelectedPosition(int position) {
+        mSelectedPosition = position;
     }
 
     @Override
@@ -89,4 +100,28 @@
         mDevices = deviceFilterPairs;
         notifyDataSetChanged();
     }
+
+    static class ViewHolder extends RecyclerView.ViewHolder {
+        private TextView mTextView;
+        private ImageView mImageView;
+        ViewHolder(View itemView) {
+            super(itemView);
+            mTextView = itemView.findViewById(android.R.id.text1);
+            mImageView = itemView.findViewById(android.R.id.icon);
+        }
+    }
+
+    private boolean isWifiDevice(int position) {
+        return mDevices.get(position).getDevice() instanceof android.net.wifi.ScanResult;
+    }
+
+    private Drawable getIcon(int resId) {
+        Drawable icon = mContext.getResources().getDrawable(resId, null);
+        icon.setTint(Color.DKGRAY);
+        return icon;
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(int position);
+    }
 }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index eab421e..e3e563d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -50,14 +50,13 @@
     }
 
     static @NonNull CharSequence getApplicationLabel(
-            @NonNull Context context, @NonNull String packageName) {
+            @NonNull Context context, @NonNull String packageName, int userId)
+            throws PackageManager.NameNotFoundException {
         final PackageManager packageManager = context.getPackageManager();
-        final ApplicationInfo appInfo;
-        try {
-            appInfo = packageManager.getApplicationInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
+
+        final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+                packageName, PackageManager.ApplicationInfoFlags.of(0), userId);
+
         return packageManager.getApplicationLabel(appInfo);
     }
 
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 1a955c4..72243f9 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -146,7 +146,7 @@
          * @param iface the name of the interface.
          * @param state the current state of the interface, or {@link #STATE_ABSENT} if the
          *              interface was removed.
-         * @param role whether the interface is in the client mode or server mode.
+         * @param role whether the interface is in client mode or server mode.
          * @param configuration the current IP configuration of the interface.
          * @hide
          */
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 9175809..f681ba1 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -28,6 +28,7 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -500,7 +501,7 @@
                         && roaming == e.roaming && defaultNetwork == e.defaultNetwork
                         && rxBytes == e.rxBytes && rxPackets == e.rxPackets
                         && txBytes == e.txBytes && txPackets == e.txPackets
-                        && operations == e.operations && iface.equals(e.iface);
+                        && operations == e.operations && TextUtils.equals(iface, e.iface);
             }
             return false;
         }
diff --git a/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
index f8a8168..39cbaf7 100644
--- a/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
@@ -102,15 +102,10 @@
     }
 }
 
-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/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index e8b3d4c..ef6f39a 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -51,6 +51,7 @@
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
 import static android.os.Trace.TRACE_TAG_NETWORK;
+import static android.system.OsConstants.ENOENT;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -122,7 +123,6 @@
 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;
@@ -221,6 +221,14 @@
     // 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 static final String COOKIE_TAG_MAP_PATH =
+            "/sys/fs/bpf/map_netd_cookie_tag_map";
+    private static final String APP_UID_STATS_MAP_PATH =
+            "/sys/fs/bpf/map_netd_app_uid_stats_map";
+    private static final String STATS_MAP_A_PATH =
+            "/sys/fs/bpf/map_netd_stats_map_A";
+    private static final String STATS_MAP_B_PATH =
+            "/sys/fs/bpf/map_netd_stats_map_B";
 
     private final Context mContext;
     private final NetworkStatsFactory mStatsFactory;
@@ -348,6 +356,10 @@
      */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
     private final IBpfMap<U32, U8> mUidCounterSetMap;
+    private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
+    private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapA;
+    private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapB;
+    private final IBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap;
 
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mUidOperations = new NetworkStats(0L, 10);
@@ -481,6 +493,10 @@
         mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
         mInterfaceMapUpdater.start();
         mUidCounterSetMap = mDeps.getUidCounterSetMap();
+        mCookieTagMap = mDeps.getCookieTagMap();
+        mStatsMapA = mDeps.getStatsMapA();
+        mStatsMapB = mDeps.getStatsMapB();
+        mAppUidStatsMap = mDeps.getAppUidStatsMap();
     }
 
     /**
@@ -554,8 +570,48 @@
             }
         }
 
-        public TagStatsDeleter getTagStatsDeleter() {
-            return NetworkStatsService::nativeDeleteTagData;
+        /** Gets the cookie tag map */
+        public IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() {
+            try {
+                return new BpfMap<CookieTagMapKey, CookieTagMapValue>(COOKIE_TAG_MAP_PATH,
+                        BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create cookie tag map: " + e);
+                return null;
+            }
+        }
+
+        /** Gets stats map A */
+        public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapA() {
+            try {
+                return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_A_PATH,
+                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create stats map A: " + e);
+                return null;
+            }
+        }
+
+        /** Gets stats map B */
+        public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapB() {
+            try {
+                return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_B_PATH,
+                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create stats map B: " + e);
+                return null;
+            }
+        }
+
+        /** Gets the uid stats map */
+        public IBpfMap<UidStatsMapKey, StatsMapValue> getAppUidStatsMap() {
+            try {
+                return new BpfMap<UidStatsMapKey, StatsMapValue>(APP_UID_STATS_MAP_PATH,
+                        BpfMap.BPF_F_RDWR, UidStatsMapKey.class, StatsMapValue.class);
+            } catch (ErrnoException e) {
+                Log.wtf(TAG, "Cannot create app uid stats map: " + e);
+                return null;
+            }
         }
     }
 
@@ -1802,6 +1858,69 @@
                 currentTime);
     }
 
+    // deleteKernelTagData can ignore ENOENT; otherwise we should log an error
+    private void logErrorIfNotErrNoent(final ErrnoException e, final String msg) {
+        if (e.errno != ENOENT) Log.e(TAG, msg, e);
+    }
+
+    private <K extends StatsMapKey, V extends StatsMapValue> void deleteStatsMapTagData(
+            IBpfMap<K, V> statsMap, int uid) {
+        try {
+            statsMap.forEach((key, value) -> {
+                if (key.uid == uid) {
+                    try {
+                        statsMap.deleteEntry(key);
+                    } catch (ErrnoException e) {
+                        logErrorIfNotErrNoent(e, "Failed to delete data(uid = " + key.uid + ")");
+                    }
+                }
+            });
+        } catch (ErrnoException e) {
+            Log.e(TAG, "FAILED to delete tag data from stats map", e);
+        }
+    }
+
+    /**
+     * Deletes uid tag data from CookieTagMap, StatsMapA, StatsMapB, and UidStatsMap
+     * @param uid
+     */
+    private void deleteKernelTagData(int uid) {
+        try {
+            mCookieTagMap.forEach((key, value) -> {
+                // If SkDestroyListener deletes the socket tag while this code is running,
+                // forEach will either restart iteration from the beginning or return null,
+                // depending on when the deletion happens.
+                // If it returns null, continue iteration to delete the data and in fact it would
+                // just iterate from first key because BpfMap#getNextKey would return first key
+                // if the current key is not exist.
+                if (value != null && value.uid == uid) {
+                    try {
+                        mCookieTagMap.deleteEntry(key);
+                    } catch (ErrnoException e) {
+                        logErrorIfNotErrNoent(e, "Failed to delete data(cookie = " + key + ")");
+                    }
+                }
+            });
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Failed to delete tag data from cookie tag map", e);
+        }
+
+        deleteStatsMapTagData(mStatsMapA, uid);
+        deleteStatsMapTagData(mStatsMapB, uid);
+
+        try {
+            mUidCounterSetMap.deleteEntry(new U32(uid));
+        } catch (ErrnoException e) {
+            logErrorIfNotErrNoent(e, "Failed to delete tag data from uid counter set map");
+        }
+
+        try {
+            mAppUidStatsMap.deleteEntry(new UidStatsMapKey(uid));
+        } catch (ErrnoException e) {
+            logErrorIfNotErrNoent(e, "Failed to delete tag data from app uid stats map");
+        }
+    }
+
     /**
      * Clean up {@link #mUidRecorder} after UID is removed.
      */
@@ -1817,10 +1936,7 @@
 
         // Clear kernel stats associated with UID
         for (int uid : uids) {
-            final int ret = mDeps.getTagStatsDeleter().deleteTagData(uid);
-            if (ret < 0) {
-                Log.w(TAG, "problem clearing counters for uid " + uid + ": " + Os.strerror(-ret));
-            }
+            deleteKernelTagData(uid);
         }
     }
 
@@ -2402,12 +2518,4 @@
     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/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
index 72230b4..4117d0f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -177,7 +177,7 @@
                 ret = 0;
                 break;
             case SparseChunk.FILL:
-                ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3];
+                ret = Byte.toUnsignedInt(mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3]);
                 break;
             default:
                 throw new IOException("Unsupported Chunk:" + mCur.toString());
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index fc82b79..2569198 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -14,6 +14,8 @@
 
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.core_core",
+        "windowExtLib",
         "SettingsLibUtils",
     ],
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 7f17d26..706aba3d 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -16,8 +16,14 @@
 
 package com.android.settingslib.activityembedding;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.core.os.BuildCompat;
+import androidx.window.embedding.SplitController;
 
 import com.android.settingslib.utils.BuildCompatUtils;
 
@@ -44,6 +50,40 @@
         return false;
     }
 
+    /**
+     * Whether the current activity is embedded in the Settings app or not.
+     *
+     * @param activity Activity that needs the check
+     */
+    public static boolean isActivityEmbedded(Activity activity) {
+        return SplitController.getInstance().isActivityEmbedded(activity);
+    }
+
+    /**
+     * Whether the current activity should hide the navigate up button.
+     *
+     * @param activity             Activity that needs the check
+     * @param isSecondLayerPage indicates if the activity(page) is shown in the 2nd layer of
+     *                             Settings app
+     */
+    public static boolean shouldHideNavigateUpButton(Activity activity, boolean isSecondLayerPage) {
+        if (!BuildCompat.isAtLeastT()) {
+            return false;
+        }
+        if (!isSecondLayerPage) {
+            return false;
+        }
+        final String shouldHideNavigateUpButton =
+                Settings.Global.getString(activity.getContentResolver(),
+                        "settings_hide_second_layer_page_navigate_up_button_in_two_pane");
+
+        if (TextUtils.isEmpty(shouldHideNavigateUpButton)
+                || Boolean.parseBoolean(shouldHideNavigateUpButton)) {
+            return isActivityEmbedded(activity);
+        }
+        return false;
+    }
+
     private ActivityEmbeddingUtils() {
     }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 684f4de..7a5ea47 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,7 +48,6 @@
         "SettingsLibCollapsingToolbarBaseActivity",
         "SettingsLibTwoTargetPreference",
         "SettingsLibSettingsTransition",
-        "SettingsLibActivityEmbedding",
         "SettingsLibButtonPreference",
         "setupdesign",
     ],
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 30748e6..d2a4de4 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -50,7 +50,7 @@
             android:tint="?android:attr/colorAccent"
             android:layout_gravity="center_vertical"
             android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
-            android:src="@android:drawable/ic_info"
+            android:src="@drawable/settingslib_ic_info"
             android:visibility="gone" />
 
         <Switch
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
deleted file mode 100644
index 139cd95..0000000
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2018 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
-    android:color="@color/ripple_color">
-
-    <item android:id="@android:id/background">
-        <layer-list android:paddingMode="stack">
-            <item
-                android:top="8dp"
-                android:bottom="8dp">
-
-                <shape>
-                    <corners android:radius="28dp"/>
-                    <solid android:color="?priv-android:attr/colorAccentPrimary"/>
-                    <size android:height="@dimen/spinner_height"/>
-                </shape>
-            </item>
-
-            <item
-                android:gravity="center|end"
-                android:width="18dp"
-                android:height="18dp"
-                android:end="8dp"
-                android:drawable="@drawable/arrow_drop_down"/>
-        </layer-list>
-    </item>
-</ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
similarity index 78%
rename from packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
rename to packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
index a342c84..cea1133 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_dropdown_view.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     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.
@@ -18,8 +18,7 @@
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/text1"
-    style="@style/SettingsSpinnerTitleBar"
+    style="@style/SettingsSpinnerDropdown"
     android:gravity="center_vertical"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@drawable/settings_spinner_dropdown_background"/>
+    android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
index 7d5b6db..4c75344 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
@@ -22,7 +22,7 @@
     android:layout_marginStart="16dp"
     android:layout_marginEnd="16dp">
 
-    <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+    <Spinner
         android:id="@+id/spinner"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml b/packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml
deleted file mode 100644
index abcf822..0000000
--- a/packages/SettingsLib/SettingsSpinner/res/values-night/colors.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <color name="ripple_color">@*android:color/material_grey_900</color>
-</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/colors.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
similarity index 72%
rename from packages/SettingsLib/SettingsSpinner/res/values/colors.xml
rename to packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
index 799b35e..8fda876 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/colors.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
@@ -15,5 +15,6 @@
 -->
 
 <resources>
-    <color name="ripple_color">?android:attr/colorControlHighlight</color>
+    <color name="settingslib_spinner_title_color">@android:color/system_neutral1_900</color>
+    <color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
new file mode 100644
index 0000000..fc3ec43
--- /dev/null
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
@@ -0,0 +1,42 @@
+<?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.
+  -->
+
+<resources>
+    <style name="SettingsSpinnerTitleBar">
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textColor">@color/settingslib_spinner_title_color</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:minHeight">@dimen/spinner_height</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">36dp</item>
+        <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
+        <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
+    </style>
+
+    <style name="SettingsSpinnerDropdown">
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
+        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
+        <item name="android:maxLines">1</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:minHeight">@dimen/spinner_height</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">36dp</item>
+        <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
+        <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
index f665f38..8ea1f9a 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
@@ -18,10 +18,8 @@
 <resources>
     <style name="SettingsSpinnerTitleBar">
         <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
-        <item name="android:textColor">@android:color/black</item>
         <item name="android:maxLines">1</item>
         <item name="android:ellipsize">marquee</item>
-        <item name="android:minHeight">@dimen/spinner_height</item>
         <item name="android:paddingStart">16dp</item>
         <item name="android:paddingEnd">36dp</item>
         <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
similarity index 77%
rename from packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
rename to packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
index 83da512..2611207 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.widget.settingsspinner;
+package com.android.settingslib.widget;
 
 import android.content.Context;
+import android.os.Build;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 
-import com.android.settingslib.widget.R;
-
 /**
- * An ArrayAdapter which was used by {@link SettingsSpinner} with settings style.
+ * An ArrayAdapter which was used by Spinner with settings style.
+ * @param <T> the data type to be loaded.
  */
 public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> {
 
@@ -43,7 +43,7 @@
     public SettingsSpinnerAdapter(Context context) {
         super(context, DEFAULT_RESOURCE);
 
-        setDropDownViewResource(DFAULT_DROPDOWN_RESOURCE);
+        setDropDownViewResource(getDropdownResource());
         mDefaultInflater = LayoutInflater.from(context);
     }
 
@@ -59,6 +59,11 @@
      * drop down view.
      */
     public View getDefaultDropDownView(int position, View convertView, ViewGroup parent) {
-        return mDefaultInflater.inflate(DFAULT_DROPDOWN_RESOURCE, parent, false /* attachToRoot */);
+        return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */);
+    }
+
+    private int getDropdownResource() {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+                ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item;
     }
 }
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index d993e44..6952875 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -20,16 +20,14 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.AdapterView;
+import android.widget.Spinner;
 
 import androidx.preference.Preference;
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
 
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
-
 /**
- * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for
+ * This preference uses Spinner & SettingsSpinnerAdapter which provide default layouts for
  * both view and drop down view of the Spinner.
  */
 public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
@@ -113,7 +111,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
+        final Spinner spinner = (Spinner) holder.findViewById(R.id.spinner);
         spinner.setAdapter(mAdapter);
         spinner.setSelection(mPosition);
         spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
deleted file mode 100644
index 14286fa..0000000
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.widget.settingsspinner;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.widget.Spinner;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.settingslib.widget.R;
-
-/**
- * A {@link Spinner} with settings style.
- *
- * The items in the SettingsSpinner come from the {@link SettingsSpinnerAdapter} associated with
- * this view.
- */
-public class SettingsSpinner extends Spinner {
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     */
-    public SettingsSpinner(Context context) {
-        super(context);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme and the supplied
-     * mode of displaying choices. <code>mode</code> may be one of
-     * {@link Spinner#MODE_DIALOG} or {@link Spinner#MODE_DROPDOWN}.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param mode Constant describing how the user will select choices from
-     *             the spinner.
-     *
-     * @see Spinner#MODE_DIALOG
-     * @see Spinner#MODE_DROPDOWN
-     */
-    public SettingsSpinner(Context context, int mode) {
-        super(context, mode);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme and the supplied
-     * attribute set.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     */
-    public SettingsSpinner(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme, the supplied
-     * attribute set, and default style attribute.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     */
-    public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setBackgroundResource(R.drawable.settings_spinner_background);
-    }
-
-    /**
-     * Constructs a new SettingsSpinner with the given context's theme, the supplied
-     * attribute set, and default styles. <code>mode</code> may be one of
-     * {@link Spinner#MODE_DIALOG} or {@link Spinner#MODE_DROPDOWN} and determines how the
-     * user will select choices from the spinner.
-     * And it also set a background resource with settings style.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param attrs The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr An attribute in the current theme that contains a
-     *                     reference to a style resource that supplies default
-     *                     values for the view. Can be 0 to not look for
-     *                     defaults.
-     * @param defStyleRes A resource identifier of a style resource that
-     *                    supplies default values for the view, used only if
-     *                    defStyleAttr is 0 or can not be found in the theme.
-     *                    Can be 0 to not look for defaults.
-     * @param mode Constant describing how the user will select choices from
-     *             the spinner.
-     *
-     * @see Spinner#MODE_DIALOG
-     * @see Spinner#MODE_DROPDOWN
-     */
-    @RequiresApi(Build.VERSION_CODES.M)
-    public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
-            int mode) {
-        super(context, attrs, defStyleAttr, defStyleRes, mode, null);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        setDropDownVerticalOffset(getMeasuredHeight() - (int) getContext().getResources()
-                .getDimension(R.dimen.spinner_padding_top_or_bottom));
-    }
-}
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
similarity index 93%
rename from packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
index 0544526..770a69d 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 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.
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
index a4c780b..ae63928 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_progress_horizontal.xml
@@ -27,11 +27,12 @@
 
     <item
         android:id="@android:id/progress">
-        <clip>
+        <scale android:scaleWidth="100%" android:useIntrinsicSizeAsMinimum="true">
             <shape>
                 <corners android:radius="8dp" />
                 <solid android:color="?android:attr/textColorPrimary" />
+                <size android:width="8dp"/>
             </shape>
-        </clip>
+        </scale>
     </item>
 </layer-list>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
new file mode 100644
index 0000000..e1764af
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="@color/settingslib_ripple_color">
+
+<item android:id="@android:id/background">
+    <layer-list android:paddingMode="stack">
+        <item
+            android:top="8dp"
+            android:bottom="8dp">
+
+            <shape>
+                <corners android:radius="28dp"/>
+                <solid android:color="@android:color/system_accent1_100"/>
+                <size android:height="@dimen/settingslib_spinner_height"/>
+            </shape>
+        </item>
+
+        <item
+            android:gravity="center|end"
+            android:width="18dp"
+            android:height="18dp"
+            android:end="12dp"
+            android:drawable="@drawable/settingslib_arrow_drop_down"/>
+    </layer-list>
+</item>
+</ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
similarity index 75%
rename from packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
index aa451ae..056fb82 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_dropdown_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 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.
@@ -17,12 +17,12 @@
 
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
-    android:color="@color/ripple_color">
+    android:color="@color/settingslib_ripple_color">
 
     <item android:id="@android:id/background">
         <shape>
-            <solid android:color="?priv-android:attr/colorAccentSecondary"/>
+            <corners android:radius="10dp"/>
+            <solid android:color="@android:color/system_accent2_100"/>
         </shape>
     </item>
 </ripple>
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
similarity index 80%
copy from packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
copy to packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
index 0544526..6ed215d 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/arrow_drop_down.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2018 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.
@@ -16,11 +16,11 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:viewportWidth="18"
-        android:viewportHeight="18"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
         android:width="24dp"
         android:height="24dp">
     <path
         android:pathData="M7 10l5 5 5 -5z"
-        android:fillColor="@android:color/black"/>
+        android:fillColor="?android:attr/textColorPrimary"/>
 </vector>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
new file mode 100644
index 0000000..7466712
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple
+xmlns:android="http://schemas.android.com/apk/res/android"
+android:color="@color/settingslib_ripple_color">
+
+    <item android:id="@android:id/background">
+        <layer-list android:paddingMode="stack">
+            <item
+                android:top="8dp"
+                android:bottom="8dp">
+
+                <shape>
+                    <corners
+                        android:radius="20dp"/>
+                    <solid
+                        android:color="?android:attr/colorPrimary"/>
+                    <stroke
+                        android:color="#1f000000"
+                        android:width="1dp"/>
+                    <size
+                        android:height="32dp"/>
+                </shape>
+            </item>
+
+            <item
+                android:gravity="center|end"
+                android:width="24dp"
+                android:height="24dp"
+                android:end="4dp"
+                android:drawable="@drawable/settingslib_arrow_drop_down"/>
+        </layer-list>
+    </item>
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 3f2b8ac..77c4533 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -47,4 +47,6 @@
     <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_200</color>
 
     <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
+
+    <color name="settingslib_ripple_color">@color/settingslib_material_grey_900</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index ec3c336..6adb789 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -69,4 +69,7 @@
     <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_700</color>
 
     <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_ripple_color">?android:attr/colorControlHighlight</color>
+    <color name="settingslib_material_grey_900">#ff212121</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index 11546c8..29fdab1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -25,4 +25,6 @@
     <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen>
     <!-- Right padding of the preference -->
     <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen>
+
+    <dimen name="settingslib_spinner_height">36dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 8e7226b..9d39911 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -45,4 +45,13 @@
         <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item>
         <item name="android:scaleY">0.5</item>
     </style>
+
+    <style name="Spinner.SettingsLib"
+           parent="android:style/Widget.Material.Spinner">
+        <item name="android:background">@drawable/settingslib_spinner_background</item>
+        <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item>
+        <item name="android:dropDownVerticalOffset">48dp</item>
+        <item name="android:layout_marginTop">16dp</item>
+        <item name="android:layout_marginBottom">8dp</item>
+    </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 7bf75bc..e9bbcc78 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -26,6 +26,7 @@
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
         <item name="android:switchStyle">@style/Switch.SettingsLib</item>
         <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
+        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
     </style>
 
     <!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index aaab0f0..fa27bb6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -26,4 +26,10 @@
     <style name="TextAppearance.CategoryTitle.SettingsLib"
            parent="@android:style/TextAppearance.DeviceDefault.Medium">
     </style>
+
+    <style name="Spinner.SettingsLib"
+           parent="android:style/Widget.Material.Spinner">
+        <item name="android:background">@drawable/settingslib_spinner_background</item>
+        <item name="android:dropDownVerticalOffset">48dp</item>
+    </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 2d881d1..8dc0f3c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -19,6 +19,7 @@
     <!-- Only using in Settings application -->
     <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
+        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
     </style>
 
     <!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml b/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
index 923d022..52d7775 100644
--- a/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
+++ b/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
@@ -34,14 +34,4 @@
         android:textAppearance="?android:attr/textAppearanceListItemSmall"
         android:textColor="?android:attr/textColorAlertDialogListItem" />
 
-    <ImageView
-        android:id="@+id/restricted_icon"
-        android:layout_width="@*android:dimen/config_restrictedIconSize"
-        android:layout_height="@*android:dimen/config_restrictedIconSize"
-        android:layout_alignParentRight="true"
-        android:scaleType="centerInside"
-        android:src="@*android:drawable/ic_info"
-        android:tint="?android:attr/colorAccent"
-        android:visibility="gone" />
-
 </RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_preference_widget_primary_switch.xml b/packages/SettingsLib/res/layout/restricted_preference_widget_primary_switch.xml
deleted file mode 100644
index 69df751..0000000
--- a/packages/SettingsLib/res/layout/restricted_preference_widget_primary_switch.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <include layout="@layout/restricted_icon"/>
-
-    <include layout="@layout/preference_widget_primary_switch"/>
-</merge>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
deleted file mode 100644
index 1520ac8..0000000
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/restricted_icon"
-        android:layout_width="@dimen/two_target_min_width"
-        android:layout_height="@*android:dimen/config_restrictedIconSize"
-        android:tint="?android:attr/colorAccent"
-        android:src="@*android:drawable/ic_info"
-        android:gravity="center" />
-    <!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
-    <Switch xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/switch_widget"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:focusable="false"
-        android:clickable="false"
-        android:background="@null" />
-</merge>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 25d6c555..d893d09 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -22,8 +22,6 @@
     <!-- The translation for disappearing security views after having solved them. -->
     <dimen name="disappear_y_translation">-32dp</dimen>
 
-    <dimen name="circle_avatar_size">190dp</dimen>
-
     <!-- Height of a user icon view -->
     <dimen name="user_icon_view_height">24dp</dimen>
     <!-- User spinner -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8965144e..014a033 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1160,6 +1160,8 @@
     <string name="battery_info_status_not_charging">Connected, not charging</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_full">Charged</string>
+    <!-- [CHAR_LIMIT=40] Battery Info screen. Value for a status item. A state which device is fully charged -->
+    <string name="battery_info_status_full_charged">Fully Charged</string>
 
     <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
     <string name="disabled_by_admin_summary_text">Controlled by admin</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 246fc8dd..b43b444 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -59,7 +59,7 @@
 
     @Override
     protected int getSecondTargetResId() {
-        return R.layout.restricted_preference_widget_primary_switch;
+        return R.layout.preference_widget_primary_switch;
     }
 
     @Override
@@ -67,7 +67,6 @@
         super.onBindViewHolder(holder);
         final View switchWidget = holder.findViewById(R.id.switchWidget);
         if (switchWidget != null) {
-            switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
             switchWidget.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 81146fa..db2a6ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -22,7 +22,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.AttributeSet;
-import android.view.View;
 
 import androidx.core.content.res.TypedArrayUtils;
 import androidx.preference.PreferenceManager;
@@ -67,23 +66,9 @@
     }
 
     @Override
-    protected int getSecondTargetResId() {
-        return R.layout.restricted_icon;
-    }
-
-    @Override
-    protected boolean shouldHideSecondTarget() {
-        return !isDisabledByAdmin();
-    }
-
-    @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
         mHelper.onBindViewHolder(holder);
-        final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
-        if (restrictedIcon != null) {
-            restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
-        }
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 342189d..c607d74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -46,7 +46,6 @@
     public RestrictedSwitchPreference(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setWidgetLayoutResource(R.layout.restricted_switch_widget);
         mHelper = new RestrictedPreferenceHelper(context, this, attrs);
         if (attrs != null) {
             final TypedArray attributes = context.obtainStyledAttributes(attrs,
@@ -108,15 +107,6 @@
             switchSummary = mRestrictedSwitchSummary;
         }
 
-        final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
-        final View switchWidget = holder.findViewById(android.R.id.switch_widget);
-        if (restrictedIcon != null) {
-            restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
-        }
-        if (switchWidget != null) {
-            switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
-        }
-
         final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
 
         if (mIconSize > 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 883e080..19114cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -144,7 +144,7 @@
      * Returns a circular icon for a user.
      */
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
-        final int iconSize = UserIconDrawable.getSizeForList(context);
+        final int iconSize = UserIconDrawable.getDefaultSize(context);
         if (user.isManagedProfile()) {
             Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
             drawable.setBounds(0, 0, iconSize, iconSize);
@@ -194,9 +194,11 @@
      * @param context the context
      * @param batteryChangedIntent battery broadcast intent received from {@link
      *                             Intent.ACTION_BATTERY_CHANGED}.
+     * @param compactStatus to present compact battery charging string if {@code true}
      * @return battery status string
      */
-    public static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
+    public static String getBatteryStatus(Context context, Intent batteryChangedIntent,
+            boolean compactStatus) {
         final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
                 BatteryManager.BATTERY_STATUS_UNKNOWN);
         final Resources res = context.getResources();
@@ -205,10 +207,14 @@
         final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
 
         if (batteryStatus.isCharged()) {
-            statusString = res.getString(R.string.battery_info_status_full);
+            statusString = res.getString(compactStatus
+                    ? R.string.battery_info_status_full_charged
+                    : R.string.battery_info_status_full);
         } else {
             if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
-                if (batteryStatus.isPluggedInWired()) {
+                if (compactStatus) {
+                    statusString = res.getString(R.string.battery_info_status_charging);
+                } else if (batteryStatus.isPluggedInWired()) {
                     switch (batteryStatus.getChargingSpeed(context)) {
                         case BatteryStatus.CHARGING_FAST:
                             statusString = res.getString(
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS b/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS
new file mode 100644
index 0000000..98f4123
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS
@@ -0,0 +1,3 @@
+# Default reviewers for this and subdirectories.
+alexflo@google.com
+chrisgollner@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index e5ea446..7db00f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -31,8 +31,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
-import com.android.settingslib.R;
-
 /**
  * Converts the user avatar icon to a circularly clipped one.
  * TODO: Move this to an internal framework class and share with the one in Keyguard.
@@ -49,9 +47,9 @@
 
     public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
         Resources res = context.getResources();
-        float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+        int iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
 
-        CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+        CircleFramedDrawable instance = new CircleFramedDrawable(icon, iconSize);
         return instance;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 035fafd..d01f2b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -49,8 +49,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.os.BuildCompat;
 
-import com.android.settingslib.R;
-
 /**
  * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
  */
@@ -120,8 +118,9 @@
      * @param context
      * @return size in pixels
      */
-    public static int getSizeForList(Context context) {
-        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    public static int getDefaultSize(Context context) {
+        return context.getResources()
+                .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
     }
 
     public UserIconDrawable() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index bd3deae..fcb56d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -50,7 +50,6 @@
 public class DreamBackend {
     private static final String TAG = "DreamBackend";
     private static final boolean DEBUG = false;
-    private final Drawable mDreamPreviewDefault;
 
     public static class DreamInfo {
         public CharSequence caption;
@@ -135,8 +134,6 @@
                 com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
         mDreamsActivatedOnDockByDefault = resources.getBoolean(
                 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)
@@ -182,9 +179,6 @@
                 dreamInfo.settingsComponentName = dreamMetadata.settingsActivity;
                 dreamInfo.previewImage = dreamMetadata.previewImage;
             }
-            if (dreamInfo.previewImage == null) {
-                dreamInfo.previewImage = mDreamPreviewDefault;
-            }
             dreamInfos.add(dreamInfo);
         }
         dreamInfos.sort(mComparator);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 93be66a..1e1dfae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -81,6 +81,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setTheme(R.style.SudThemeGlifV3_DayNight);
         ThemeHelper.trySetDynamicColor(this);
         setContentView(R.layout.avatar_picker);
         setUpButtons();
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f8bb38b..5862f60 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -16,19 +16,17 @@
 
 package com.android.settingslib.users;
 
-import android.annotation.NonNull;
 import android.app.Activity;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.util.Log;
 import android.widget.ImageView;
 
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.R;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -114,10 +112,10 @@
     private void onDefaultIconSelected(int tintColor) {
         try {
             ThreadUtils.postOnBackgroundThread(() -> {
+                Resources res = mActivity.getResources();
                 Drawable drawable =
-                        UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor);
-                Bitmap bitmap = convertToBitmap(drawable,
-                        (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size));
+                        UserIcons.getDefaultUserIconInColor(res, tintColor);
+                Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, drawable);
 
                 ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
             }).get();
@@ -126,14 +124,6 @@
         }
     }
 
-    private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) {
-        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        icon.setBounds(0, 0, size, size);
-        icon.draw(canvas);
-        return bitmap;
-    }
-
     private void onPhotoCropped(final Uri data) {
         ThreadUtils.postOnBackgroundThread(() -> {
             InputStream imageStream = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 4ab6542..9ef6bdf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -43,6 +43,31 @@
     private static final int INVALID_RSSI = -127;
 
     /**
+     * The intent action shows Wi-Fi dialog to connect Wi-Fi network.
+     * <p>
+     * Input: The calling package should put the chosen
+     * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+     * the {@link #EXTRA_CHOSEN_WIFI_ENTRY_KEY}.
+     * <p>
+     * Output: Nothing.
+     */
+    @VisibleForTesting
+    static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
+
+    /**
+     * Specify a key that indicates the WifiEntry to be configured.
+     */
+    @VisibleForTesting
+    static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+
+    /**
+     * The lookup key for a boolean that indicates whether a chosen WifiEntry request to connect to.
+     * {@code true} means a chosen WifiEntry request to connect to.
+     */
+    @VisibleForTesting
+    static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
+
+    /**
      * The intent action shows network details settings to allow configuration of Wi-Fi.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -325,6 +350,19 @@
     }
 
     /**
+     * Returns the Intent for Wi-Fi dialog.
+     *
+     * @param key              The Wi-Fi entry key
+     * @param connectForCaller True if a chosen WifiEntry request to connect to
+     */
+    public static Intent getWifiDialogIntent(String key, boolean connectForCaller) {
+        final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+        intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, key);
+        intent.putExtra(EXTRA_CONNECT_FOR_CALLER, connectForCaller);
+        return intent;
+    }
+
+    /**
      * Returns the Intent for Wi-Fi network details settings.
      *
      * @param key The Wi-Fi entry key
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index 53a382a..b81d13d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -22,14 +22,12 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.Spinner;
 
 import androidx.preference.PreferenceViewHolder;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,7 +40,7 @@
 
     private Context mContext;
     private PreferenceViewHolder mViewHolder;
-    private SettingsSpinner mSpinner;
+    private Spinner mSpinner;
     private SettingsSpinnerPreference mSpinnerPreference;
 
     @Before
@@ -53,7 +51,7 @@
         final View rootView = inflater.inflate(mSpinnerPreference.getLayoutResource(),
                 new LinearLayout(mContext), false /* attachToRoot */);
         mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
-        mSpinner = (SettingsSpinner) mViewHolder.findViewById(R.id.spinner);
+        mSpinner = (Spinner) mViewHolder.findViewById(R.id.spinner);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index 4e2b63b..9c16740 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -53,13 +53,7 @@
         mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
                 com.android.settingslib.R.layout.preference_two_target, null));
         mWidgetView = mHolder.itemView.findViewById(android.R.id.widget_frame);
-        inflater.inflate(R.layout.restricted_preference_widget_primary_switch, mWidgetView, true);
-    }
-
-    @Test
-    public void createNewPreference_shouldSetLayout() {
-        assertThat(mPreference.getWidgetLayoutResource())
-                .isEqualTo(R.layout.restricted_preference_widget_primary_switch);
+        inflater.inflate(R.layout.preference_widget_primary_switch, mWidgetView, true);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 62de66e..09b2a2e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -320,28 +320,47 @@
         final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
         final Resources resources = mContext.getResources();
 
-        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
                 resources.getString(R.string.battery_info_status_full));
     }
 
     @Test
+    public void getBatteryStatus_statusIsFullAndUseCompactStatus_returnFullyChargedString() {
+        final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+                resources.getString(R.string.battery_info_status_full_charged));
+    }
+
+    @Test
     public void getBatteryStatus_batteryLevelIs100_returnFullString() {
         final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
                 BatteryManager.BATTERY_STATUS_FULL);
         final Resources resources = mContext.getResources();
 
-        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
                 resources.getString(R.string.battery_info_status_full));
     }
 
     @Test
+    public void getBatteryStatus_batteryLevelIs100AndUseCompactStatus_returnFullyString() {
+        final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_FULL);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+                resources.getString(R.string.battery_info_status_full_charged));
+    }
+
+    @Test
     public void getBatteryStatus_batteryLevel99_returnChargingString() {
         final Intent intent = new Intent();
         intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
         intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
         final Resources resources = mContext.getResources();
 
-        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
                 resources.getString(R.string.battery_info_status_charging));
     }
 
@@ -352,7 +371,29 @@
         intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
         final Resources resources = mContext.getResources();
 
-        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
                 resources.getString(R.string.battery_info_status_charging_wireless));
     }
+
+    @Test
+    public void getBatteryStatus_chargingAndUseCompactStatus_returnCompactString() {
+        final Intent intent = new Intent();
+        intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+                resources.getString(R.string.battery_info_status_charging));
+    }
+
+    @Test
+    public void getBatteryStatus_chargingWirelessAndUseCompactStatus_returnCompactString() {
+        final Intent intent = new Intent();
+        intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
+                resources.getString(R.string.battery_info_status_charging));
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index d7b366e..bb6b293 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -206,9 +206,8 @@
 
         when(config.isMandatoryCodec()).thenReturn(false);
         when(config.getCodecType()).thenReturn(4);
-        when(config.getCodecName()).thenReturn("LDAC");
         assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(
-                String.format(KNOWN_CODEC_LABEL, config.getCodecName()));
+                String.format(KNOWN_CODEC_LABEL, "LDAC"));
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index d53a3e8..298ee90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,6 +28,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
+import android.os.Parcel;
 import android.os.ParcelUuid;
 
 import org.junit.Before;
@@ -60,9 +61,9 @@
     private final static Map<Integer, ParcelUuid> CAP_GROUP2 =
             Map.of(2, BluetoothUuid.CAP);
     private final BluetoothClass DEVICE_CLASS_1 =
-        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass DEVICE_CLASS_2 =
-        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
     @Mock
@@ -92,6 +93,16 @@
     private HearingAidDeviceManager mHearingAidDeviceManager;
     private Context mContext;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 7be176a..a8e6075 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -28,6 +28,7 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.os.Parcel;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,7 +49,7 @@
     private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
     private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
     private final BluetoothClass DEVICE_CLASS =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
     @Mock
@@ -67,6 +68,16 @@
     private HearingAidDeviceManager mHearingAidDeviceManager;
     private Context mContext;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 6f7f73a..c122a37 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.os.Parcel;
 
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -65,9 +66,9 @@
     private static final String ROUTER_ID_3 = "RouterId_3";
     private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
     private final BluetoothClass mHeadreeClass =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass mCarkitClass =
-            new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
+            createBtClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
 
     @Mock
     private BluetoothDevice mDevice1;
@@ -118,6 +119,16 @@
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
 
+    private BluetoothClass createBtClass(int deviceClass) {
+        Parcel p = Parcel.obtain();
+        p.writeInt(deviceClass);
+        p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+        BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+        p.recycle();
+        return bluetoothClass;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index e7b3fe9..6956105 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -156,6 +156,27 @@
     }
 
     @Test
+    public void getWifiDialogIntent_returnsCorrectValues() {
+        String key = "test_key";
+
+        // Test that connectForCaller is true.
+        Intent intent = WifiUtils.getWifiDialogIntent(key, true /* connectForCaller */);
+
+        assertThat(intent.getAction()).isEqualTo(WifiUtils.ACTION_WIFI_DIALOG);
+        assertThat(intent.getStringExtra(WifiUtils.EXTRA_CHOSEN_WIFI_ENTRY_KEY)).isEqualTo(key);
+        assertThat(intent.getBooleanExtra(WifiUtils.EXTRA_CONNECT_FOR_CALLER, true))
+                .isEqualTo(true /* connectForCaller */);
+
+        // Test that connectForCaller is false.
+        intent = WifiUtils.getWifiDialogIntent(key, false /* connectForCaller */);
+
+        assertThat(intent.getAction()).isEqualTo(WifiUtils.ACTION_WIFI_DIALOG);
+        assertThat(intent.getStringExtra(WifiUtils.EXTRA_CHOSEN_WIFI_ENTRY_KEY)).isEqualTo(key);
+        assertThat(intent.getBooleanExtra(WifiUtils.EXTRA_CONNECT_FOR_CALLER, true))
+                .isEqualTo(false /* connectForCaller */);
+    }
+
+    @Test
     public void getWifiDetailsSettingsIntent_returnsCorrectValues() {
         final String key = "test_key";
 
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index 3b7fbc7..c7e96bc 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -69,7 +69,7 @@
     }
 
     @Implementation
-    protected boolean removeActiveDevice(@BluetoothAdapter.ActiveDeviceUse int profiles) {
+    protected boolean removeActiveDevice(int profiles) {
         if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
                 && profiles != ACTIVE_DEVICE_ALL) {
             return false;
@@ -78,8 +78,7 @@
     }
 
     @Implementation
-    protected boolean setActiveDevice(BluetoothDevice device,
-            @BluetoothAdapter.ActiveDeviceUse int profiles) {
+    protected boolean setActiveDevice(BluetoothDevice device, int profiles) {
         if (device == null) {
             return false;
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 741fe4f..adac31e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -110,7 +110,7 @@
     <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
     <uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" />
     <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
-    <uses-permission android:name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
+    <uses-permission android:name="android.permission.TRIGGER_LOST_MODE" />
     <!-- ACCESS_BACKGROUND_LOCATION is needed for testing purposes only. -->
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <!-- ACCESS_MTP is needed for testing purposes only. -->
@@ -536,6 +536,9 @@
     <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
     <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
+    <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover
+         P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. -->
+    <uses-permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN" />
 
     <!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
     <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
@@ -585,6 +588,9 @@
     <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
     <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
 
+    <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
+    <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+
     <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/>
 
     <!-- Permission required for CTS test - ResourceObserverNativeTest -->
@@ -655,6 +661,9 @@
     <!-- Permission required for CTS test - CaptioningManagerTest -->
     <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
 
+    <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+    <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b1cfb11..8cc9e00 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -112,6 +112,7 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
+        "com.google.android.material_material",
         "kotlinx_coroutines_android",
         "kotlinx_coroutines",
         "iconloader_base",
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 46adfeb..f7bcf1f 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -39,5 +39,5 @@
     ],
 
     manifest: "AndroidManifest.xml",
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 4540b77..c3f6a5d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -48,9 +48,16 @@
  * nicely into the starting window.
  */
 class ActivityLaunchAnimator(
-    private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS)
+    /** The animator used when animating a View into an app. */
+    private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
+
+    /** The animator used when animating a Dialog into an app. */
+    // TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
+    // TIMINGS.contentBeforeFadeOutDuration.
+    private val dialogToAppAnimator: LaunchAnimator = LaunchAnimator(DIALOG_TIMINGS, INTERPOLATORS)
 ) {
     companion object {
+        /** The timings when animating a View into an app. */
         @JvmField
         val TIMINGS = LaunchAnimator.Timings(
             totalDuration = 500L,
@@ -60,6 +67,17 @@
             contentAfterFadeInDuration = 183L
         )
 
+        /**
+         * The timings when animating a Dialog into an app. We need to wait at least 200ms before
+         * showing the app (which is under the dialog window) so that the dialog window dim is fully
+         * faded out, to avoid flicker.
+         */
+        val DIALOG_TIMINGS = TIMINGS.copy(
+            contentBeforeFadeOutDuration = 200L,
+            contentAfterFadeInDelay = 200L
+        )
+
+        /** The interpolators when animating a View or a dialog into an app. */
         val INTERPOLATORS = LaunchAnimator.Interpolators(
             positionInterpolator = Interpolators.EMPHASIZED,
             positionXInterpolator = createPositionXInterpolator(),
@@ -298,10 +316,17 @@
         }
 
         /**
+         * Whether this controller is controlling a dialog launch. This will be used to adapt the
+         * timings, making sure we don't show the app until the dialog dim had the time to fade out.
+         */
+        // TODO(b/218989950): Remove this.
+        val isDialogLaunch: Boolean
+            get() = false
+
+        /**
          * The intent was started. If [willAnimate] is false, nothing else will happen and the
          * animation will not be started.
          */
-        @JvmDefault
         fun onIntentStarted(willAnimate: Boolean) {}
 
         /**
@@ -309,7 +334,6 @@
          * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
          * before the cancellation.
          */
-        @JvmDefault
         fun onLaunchAnimationCancelled() {}
     }
 
@@ -317,7 +341,9 @@
     inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
         private val launchContainer = controller.launchContainer
         private val context = launchContainer.context
-        private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
+        private val transactionApplierView =
+            controller.openingWindowSyncView ?: controller.launchContainer
+        private val transactionApplier = SyncRtSurfaceTransactionApplier(transactionApplierView)
 
         private val matrix = Matrix()
         private val invertMatrix = Matrix()
@@ -405,6 +431,13 @@
             val callback = this@ActivityLaunchAnimator.callback!!
             val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
 
+            // Make sure we use the modified timings when animating a dialog into an app.
+            val launchAnimator = if (controller.isDialogLaunch) {
+                dialogToAppAnimator
+            } else {
+                launchAnimator
+            }
+
             // TODO(b/184121838): We should somehow get the top and bottom radius of the window
             // instead of recomputing isExpandingFullyAbove here.
             val isExpandingFullyAbove =
@@ -440,19 +473,29 @@
                     progress: Float,
                     linearProgress: Float
                 ) {
-                    applyStateToWindow(window, state)
+                    // Apply the state to the window only if it is visible, i.e. when the expanding
+                    // view is *not* visible.
+                    if (!state.visible) {
+                        applyStateToWindow(window, state)
+                    }
                     navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+
                     listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
                     delegate.onLaunchAnimationProgress(state, progress, linearProgress)
                 }
             }
 
-            // We draw a hole when the additional layer is fading out to reveal the opening window.
             animation = launchAnimator.startAnimation(
                 controller, endState, windowBackgroundColor, drawHole = true)
         }
 
         private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) {
+            if (transactionApplierView.viewRootImpl == null) {
+                // If the view root we synchronize with was detached, don't apply any transaction
+                // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+                return
+            }
+
             val screenBounds = window.screenSpaceBounds
             val centerX = (screenBounds.left + screenBounds.right) / 2f
             val centerY = (screenBounds.top + screenBounds.bottom) / 2f
@@ -510,6 +553,12 @@
             state: LaunchAnimator.State,
             linearProgress: Float
         ) {
+            if (transactionApplierView.viewRootImpl == null) {
+                // If the view root we synchronize with was detached, don't apply any transaction
+                // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+                return
+            }
+
             val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress,
                 ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 3051d80..a3c5649 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -19,7 +19,6 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.app.ActivityManager
 import android.app.Dialog
 import android.graphics.Color
 import android.graphics.Rect
@@ -28,12 +27,12 @@
 import android.util.Log
 import android.util.MathUtils
 import android.view.GhostView
-import android.view.SurfaceControl
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewRootImpl
+import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.widget.FrameLayout
 import kotlin.math.roundToInt
 
@@ -42,12 +41,17 @@
 /**
  * A class that allows dialogs to be started in a seamless way from a view that is transforming
  * nicely into the starting dialog.
+ *
+ * This animator also allows to easily animate a dialog into an activity.
+ *
+ * @see showFromView
+ * @see showFromDialog
+ * @see createActivityLaunchController
  */
 class DialogLaunchAnimator @JvmOverloads constructor(
     private val dreamManager: IDreamManager,
     private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
-    // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed
-    private var forceDisableSynchronization: Boolean = ActivityManager.isLowRamDeviceStatic()
+    private val isForTesting: Boolean = false
 ) {
     private companion object {
         private val TIMINGS = ActivityLaunchAnimator.TIMINGS
@@ -113,7 +117,7 @@
                 dialog = dialog,
                 animateBackgroundBoundsChange,
                 animatedParent,
-                forceDisableSynchronization
+                isForTesting
         )
 
         openedDialogs.add(animatedDialog)
@@ -141,6 +145,100 @@
     }
 
     /**
+     * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the
+     * dialog that contains [View]. Note that the dialog must have been show using [showFromView]
+     * and be currently showing, otherwise this will return null.
+     *
+     * The returned controller will take care of dismissing the dialog at the right time after the
+     * activity started, when the dialog to app animation is done (or when it is cancelled). If this
+     * method returns null, then the dialog won't be dismissed.
+     *
+     * @param view any view inside the dialog to animate.
+     */
+    @JvmOverloads
+    fun createActivityLaunchController(
+        view: View,
+        cujType: Int? = null
+    ): ActivityLaunchAnimator.Controller? {
+        val animatedDialog = openedDialogs
+            .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
+            ?: return null
+
+        // At this point, we know that the intent of the caller is to dismiss the dialog to show
+        // an app, so we disable the exit animation into the touch surface because we will never
+        // want to run it anyways.
+        animatedDialog.exitAnimationDisabled = true
+
+        val dialog = animatedDialog.dialog
+
+        // Don't animate if the dialog is not showing.
+        if (!dialog.isShowing) {
+            return null
+        }
+
+        val dialogContentWithBackground = animatedDialog.dialogContentWithBackground ?: return null
+        val controller =
+            ActivityLaunchAnimator.Controller.fromView(dialogContentWithBackground, cujType)
+                ?: return null
+
+        // Wrap the controller into one that will instantly dismiss the dialog when the animation is
+        // done or dismiss it normally (fading it out) if the animation is cancelled.
+        return object : ActivityLaunchAnimator.Controller by controller {
+            override val isDialogLaunch = true
+
+            override fun onIntentStarted(willAnimate: Boolean) {
+                controller.onIntentStarted(willAnimate)
+
+                if (!willAnimate) {
+                    dialog.dismiss()
+                }
+            }
+
+            override fun onLaunchAnimationCancelled() {
+                controller.onLaunchAnimationCancelled()
+                enableDialogDismiss()
+                dialog.dismiss()
+            }
+
+            override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+                controller.onLaunchAnimationStart(isExpandingFullyAbove)
+
+                // Make sure the dialog is not dismissed during the animation.
+                disableDialogDismiss()
+
+                // If this dialog was shown from a cascade of other dialogs, make sure those ones
+                // are dismissed too.
+                animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
+
+                // Remove the dim.
+                dialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+            }
+
+            override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+                controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+                // Hide the dialog then dismiss it to instantly dismiss it without playing the
+                // animation.
+                dialog.hide()
+                enableDialogDismiss()
+                dialog.dismiss()
+            }
+
+            private fun disableDialogDismiss() {
+                dialog.setDismissOverride { /* Do nothing */ }
+            }
+
+            private fun enableDialogDismiss() {
+                // We don't set the override to null given that [AnimatedDialog.OnDialogDismissed]
+                // will still properly dismiss the dialog but will also make sure to clean up
+                // everything (like making sure that the touched view that triggered the dialog is
+                // made VISIBLE again).
+                dialog.setDismissOverride(animatedDialog::onDialogDismissed)
+            }
+        }
+    }
+
+    /**
      * Ensure that all dialogs currently shown won't animate into their touch surface when
      * dismissed.
      *
@@ -358,6 +456,21 @@
         // Make sure the dialog is visible instantly and does not do any window animation.
         window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
 
+        // Ensure that the animation is not clipped by the display cut-out when animating this
+        // dialog into an app.
+        window.attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        window.attributes = window.attributes
+
+        // We apply the insets ourselves to make sure that the paddings are set on the correct
+        // View.
+        window.setDecorFitsSystemWindows(false)
+        val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
+        viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
+            val insets = windowInsets.getInsets(WindowInsets.Type.displayCutout())
+            view.setPadding(insets.left, insets.top, insets.right, insets.bottom)
+            WindowInsets.CONSUMED
+        }
+
         // Start the animation once the background view is properly laid out.
         dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
             override fun onLayoutChange(
@@ -421,45 +534,12 @@
      * (or inversely, removed from the UI when the touch surface is made visible).
      */
     private fun synchronizeNextDraw(then: () -> Unit) {
-        if (forceDisableSynchronization ||
-                !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
-                !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
-            // No need to synchronize if either the touch surface or dialog view is not attached
-            // to a window.
+        if (forceDisableSynchronization) {
             then()
             return
         }
 
-        // Consume the next frames of both view roots to make sure the ghost view is drawn at
-        // exactly the same time as when the touch surface is made invisible.
-        var remainingTransactions = 0
-        val mergedTransactions = SurfaceControl.Transaction()
-
-        fun onTransaction(transaction: SurfaceControl.Transaction?) {
-            remainingTransactions--
-            transaction?.let { mergedTransactions.merge(it) }
-
-            if (remainingTransactions == 0) {
-                mergedTransactions.apply()
-                then()
-            }
-        }
-
-        fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
-            if (viewRootImpl.consumeNextDraw(::onTransaction)) {
-                remainingTransactions++
-
-                // Make sure we trigger a traversal.
-                viewRootImpl.view.invalidate()
-            }
-        }
-
-        consumeNextDraw(touchSurface.viewRootImpl)
-        consumeNextDraw(decorView.viewRootImpl)
-
-        if (remainingTransactions == 0) {
-            then()
-        }
+        ViewRootSync.synchronizeNextDraw(touchSurface, decorView, then)
     }
 
     private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
@@ -523,7 +603,7 @@
         )
     }
 
-    private fun onDialogDismissed() {
+    fun onDialogDismissed() {
         if (Looper.myLooper() != Looper.getMainLooper()) {
             dialog.context.mainExecutor.execute { onDialogDismissed() }
             return
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index 77386cf..a4c5c30 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -77,8 +77,8 @@
          * This will be used to:
          *  - Get the associated [Context].
          *  - Compute whether we are expanding fully above the launch container.
-         *  - Apply surface transactions in sync with RenderThread when animating an activity
-         *    launch.
+         *  - Get to overlay to which we initially put the window background layer, until the
+         *    opening window is made visible (see [openingWindowSyncView]).
          *
          * This container can be changed to force this [Controller] to animate the expanding view
          * inside a different location, for instance to ensure correct layering during the
@@ -87,6 +87,18 @@
         var launchContainer: ViewGroup
 
         /**
+         * The [View] with which the opening app window should be synchronized with once it starts
+         * to be visible.
+         *
+         * We will also move the window background layer to this view's overlay once the opening
+         * window is visible.
+         *
+         * If null, this will default to [launchContainer].
+         */
+        val openingWindowSyncView: View?
+            get() = null
+
+        /**
          * Return the [State] of the view that will be animated. We will animate from this state to
          * the final window state.
          *
@@ -100,11 +112,9 @@
          * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
          * fully above the [launchContainer].
          */
-        @JvmDefault
         fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
 
         /** The animation made progress and the expandable view [state] should be updated. */
-        @JvmDefault
         fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
 
         /**
@@ -112,7 +122,6 @@
          * called previously. This is typically used to clean up the resources initialized when the
          * animation was started.
          */
-        @JvmDefault
         fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
     }
 
@@ -154,7 +163,7 @@
     }
 
     /** The timings (durations and delays) used by this animator. */
-    class Timings(
+    data class Timings(
         /** The total duration of the animation. */
         val totalDuration: Long,
 
@@ -257,8 +266,17 @@
         animator.duration = timings.totalDuration
         animator.interpolator = LINEAR
 
+        // Whether we should move the [windowBackgroundLayer] into the overlay of
+        // [Controller.openingWindowSyncView] once the opening app window starts to be visible.
+        val openingWindowSyncView = controller.openingWindowSyncView
+        val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay
+        val moveBackgroundLayerWhenAppIsVisible = openingWindowSyncView != null &&
+            openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl
+
         val launchContainerOverlay = launchContainer.overlay
         var cancelled = false
+        var movedBackgroundLayer = false
+
         animator.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
                 if (DEBUG) {
@@ -278,6 +296,10 @@
                 }
                 controller.onLaunchAnimationEnd(isExpandingFullyAbove)
                 launchContainerOverlay.remove(windowBackgroundLayer)
+
+                if (moveBackgroundLayerWhenAppIsVisible) {
+                    openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                }
             }
         })
 
@@ -318,11 +340,29 @@
                 timings.contentBeforeFadeOutDuration
             ) < 1
 
+            if (moveBackgroundLayerWhenAppIsVisible && !state.visible && !movedBackgroundLayer) {
+                // The expanding view is not visible, so the opening app is visible. If this is the
+                // first frame when it happens, trigger a one-off sync and move the background layer
+                // in its new container.
+                movedBackgroundLayer = true
+
+                launchContainerOverlay.remove(windowBackgroundLayer)
+                openingWindowSyncViewOverlay!!.add(windowBackgroundLayer)
+
+                ViewRootSync.synchronizeNextDraw(launchContainer, openingWindowSyncView, then = {})
+            }
+
+            val container = if (movedBackgroundLayer) {
+                openingWindowSyncView!!
+            } else {
+                controller.launchContainer
+            }
+
             applyStateToWindowBackgroundLayer(
                 windowBackgroundLayer,
                 state,
                 linearProgress,
-                launchContainer,
+                container,
                 drawHole
             )
             controller.onLaunchAnimationProgress(state, progress, linearProgress)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
new file mode 100644
index 0000000..5b3e45c
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
@@ -0,0 +1,75 @@
+package com.android.systemui.animation
+
+import android.app.ActivityManager
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewRootImpl
+
+/** A util class to synchronize 2 view roots. */
+// TODO(b/200284684): Remove this class.
+object ViewRootSync {
+    // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed
+    private val forceDisableSynchronization = ActivityManager.isLowRamDeviceStatic()
+
+    /**
+     * Synchronize the next draw between the view roots of [view] and [otherView], then run [then].
+     *
+     * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the
+     * next transactions) or disabled (temporarily, on low ram devices). In this case, [then] will
+     * be called without synchronizing.
+     */
+    fun synchronizeNextDraw(
+        view: View,
+        otherView: View,
+        then: () -> Unit
+    ) {
+        if (forceDisableSynchronization ||
+            !view.isAttachedToWindow || view.viewRootImpl == null ||
+            !otherView.isAttachedToWindow || otherView.viewRootImpl == null ||
+            view.viewRootImpl == otherView.viewRootImpl) {
+            // No need to synchronize if either the touch surface or dialog view is not attached
+            // to a window.
+            then()
+            return
+        }
+
+        // Consume the next frames of both view roots to make sure the ghost view is drawn at
+        // exactly the same time as when the touch surface is made invisible.
+        var remainingTransactions = 0
+        val mergedTransactions = SurfaceControl.Transaction()
+
+        fun onTransaction(transaction: SurfaceControl.Transaction?) {
+            remainingTransactions--
+            transaction?.let { mergedTransactions.merge(it) }
+
+            if (remainingTransactions == 0) {
+                mergedTransactions.apply()
+                then()
+            }
+        }
+
+        fun consumeNextDraw(viewRootImpl: ViewRootImpl) {
+            if (viewRootImpl.consumeNextDraw(::onTransaction)) {
+                remainingTransactions++
+
+                // Make sure we trigger a traversal.
+                viewRootImpl.view.invalidate()
+            }
+        }
+
+        consumeNextDraw(view.viewRootImpl)
+        consumeNextDraw(otherView.viewRootImpl)
+
+        if (remainingTransactions == 0) {
+            then()
+        }
+    }
+
+    /**
+     * A Java-friendly API for [synchronizeNextDraw].
+     */
+    @JvmStatic
+    fun synchronizeNextDraw(view: View, otherView: View, then: Runnable) {
+        synchronizeNextDraw(view, otherView, then::run)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 208825c..d8b050a 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -76,14 +76,14 @@
 
 enum class Style(internal val coreSpec: CoreSpec) {
     SPRITZ(CoreSpec(
-            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
-            a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
-            a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 12.0)),
+            a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
+            a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
             n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
-            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0))
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0))
     )),
     TONAL_SPOT(CoreSpec(
-            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+            a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 32.0)),
             a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
             a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)),
             n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
@@ -91,17 +91,17 @@
     )),
     VIBRANT(CoreSpec(
             a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
-            a2 = TonalSpec(Hue(HueStrategy.ADD, 10.0), Chroma(ChromaStrategy.EQ, 24.0)),
-            a3 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.GTE, 32.0)),
+            a2 = TonalSpec(Hue(HueStrategy.ADD, 15.0), Chroma(ChromaStrategy.EQ, 24.0)),
+            a3 = TonalSpec(Hue(HueStrategy.ADD, 30.0), Chroma(ChromaStrategy.GTE, 32.0)),
             n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
             n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
     )),
     EXPRESSIVE(CoreSpec(
-            a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 40.0), Chroma(ChromaStrategy.GTE, 64.0)),
-            a2 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.EQ, 24.0)),
-            a3 = TonalSpec(Hue(HueStrategy.SUBTRACT, 80.0), Chroma(ChromaStrategy.GTE, 64.0)),
-            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
-            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0))
+            a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 60.0), Chroma(ChromaStrategy.GTE, 64.0)),
+            a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 30.0), Chroma(ChromaStrategy.EQ, 24.0)),
+            a3 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+            n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 12.0)),
+            n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
     )),
     RAINBOW(CoreSpec(
             a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
index 757ed76..b33c544 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -91,6 +91,9 @@
      *         areas, false otherwise
      */
     static boolean isInAreas(ArrayList<Rect> areas, View view) {
+        if (areas.isEmpty()) {
+            return true;
+        }
         for (Rect area : areas) {
             if (isInArea(area, view)) {
                 return true;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index cf69512..6352f81 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,16 +1,22 @@
 # Preserve line number information for debugging stack traces.
 -keepattributes SourceFile,LineNumberTable
 
+-keep class com.android.systemui.recents.OverviewProxyRecentsImpl
+-keep class com.android.systemui.statusbar.car.CarStatusBar
+-keep class com.android.systemui.statusbar.phone.StatusBar
+-keep class com.android.systemui.statusbar.tv.TvStatusBar
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
 -keep class com.android.systemui.tv.TvSystemUIFactory
+-keep class * extends com.android.systemui.CoreStartable
+-keep class * implements com.android.systemui.CoreStartable$Injector
 
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
 }
 
 -keep class ** extends androidx.preference.PreferenceFragment
-
+-keep class com.android.systemui.tuner.*
 -keep class com.android.systemui.plugins.** {
     *;
 }
@@ -19,6 +25,10 @@
 }
 -keep class androidx.core.app.CoreComponentFactory
 
+-keep public class * extends com.android.systemui.CoreStartable {
+    public <init>(android.content.Context);
+}
+
 # Keep the wm shell lib
 -keep class com.android.wm.shell.*
 # Keep the protolog group methods that are called by the generated code
@@ -26,6 +36,11 @@
     *;
 }
 
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.Dagger** { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.Dagger** { !synthetic *; }
+
 # Allows proguard to make private and protected methods and fields public as
 # part of optimization. This lets proguard inline trivial getter/setter methods.
 -allowaccessmodification
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index 9891156..a751f58 100644
--- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -15,6 +15,6 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@android:color/system_neutral1_800" />
+    <solid android:color="@color/material_dynamic_neutral20" />
     <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
 </shape>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index dad4c19..b98f413 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -123,6 +123,7 @@
     <dimen name="bouncer_user_switcher_icon_size">190dp</dimen>
     <dimen name="bouncer_user_switcher_icon_size_plus_margin">222dp</dimen>
 
+    <dimen name="user_switcher_fullscreen_horizontal_gap">64dp</dimen>
     <dimen name="user_switcher_icon_selected_width">8dp</dimen>
     <dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen>
     <dimen name="user_switcher_fullscreen_button_padding">12dp</dimen>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index ff7cbae..f7b0982 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
     limitations under the License.
 -->
 <color xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@android:color/system_accent1_500" />
+    android:color="@color/material_dynamic_primary50" />
 
diff --git a/core/res/res/drawable/default_dream_preview.xml b/packages/SystemUI/res/drawable/ic_chevron_icon.xml
similarity index 60%
copy from core/res/res/drawable/default_dream_preview.xml
copy to packages/SystemUI/res/drawable/ic_chevron_icon.xml
index bf4a04b..acbbbcb 100644
--- a/core/res/res/drawable/default_dream_preview.xml
+++ b/packages/SystemUI/res/drawable/ic_chevron_icon.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+
 <!--
   ~ Copyright (C) 2022 The Android Open Source Project
   ~
@@ -15,6 +15,14 @@
   ~ limitations under the License.
   -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="@android:color/white"/>
-</shape>
\ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="31dp"
+    android:viewportWidth="18"
+    android:viewportHeight="31">
+  <path
+      android:pathData="M0.0061,27.8986L2.6906,30.5831L17.9219,15.3518L2.6906,0.1206L0.0061,2.8051L12.5338,15.3518"
+      android:strokeAlpha="0.7"
+      android:fillColor="#FFFFFF"
+      android:fillAlpha="0.7"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
index 3a228d5..e1b99ce 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
+++ b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
@@ -30,7 +30,7 @@
                     android:radius="28dp"/>
                 <size
                     android:height="64dp"/>
-                <solid android:color="@*android:color/system_accent1_200" />
+                <solid android:color="@color/material_dynamic_primary80" />
             </shape>
         </clip>
     </item>
diff --git a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
deleted file mode 100644
index e456e29..0000000
--- a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <gradient
-        android:angle="90"
-        android:startColor="#ff000000"
-        android:endColor="#00000000"
-        android:type="linear" />
-</shape>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
new file mode 100644
index 0000000..0544b871
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white"/>
+            <corners android:radius="18dp"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="18dp"/>
+            <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
index a3e289a..e06bfdc 100644
--- a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -22,10 +22,6 @@
     android:scrollbarAlwaysDrawVerticalTrack="true"
     android:scrollIndicators="top|bottom"
     android:fillViewport="true"
-    android:paddingTop="@dimen/dialog_button_bar_top_padding"
-    android:paddingStart="@dimen/dialog_side_padding"
-    android:paddingEnd="@dimen/dialog_side_padding"
-    android:paddingBottom="@dimen/dialog_bottom_padding"
     style="?android:attr/buttonBarStyle">
     <com.android.internal.widget.ButtonBarLayout
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
index f280cbd..ca8fadd 100644
--- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -83,9 +83,15 @@
             android:layout_height="wrap_content" />
     </FrameLayout>
 
-    <include
+    <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        layout="@layout/alert_dialog_button_bar_systemui" />
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding">
+        <include
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            layout="@layout/alert_dialog_button_bar_systemui" />
+    </FrameLayout>
 
 </com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 5d80da8..e1dbe69 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -113,4 +113,16 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
 
+    <ImageView
+        android:id="@+id/chevron_icon"
+        android:autoMirrored="true"
+        android:src="@drawable/ic_chevron_icon"
+        android:visibility="invisible"
+        android:layout_width="@dimen/control_chevron_icon_size"
+        android:layout_height="@dimen/control_chevron_icon_size"
+        android:clickable="false"
+        android:focusable="false"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 330f515..8e83b4a 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -34,34 +34,5 @@
         app:layout_constraintBottom_toBottomOf="parent"
         />
 
-    <com.android.systemui.dreams.DreamOverlayStatusBarView
-        android:id="@+id/dream_overlay_status_bar"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/dream_overlay_status_bar_height"
-        android:paddingEnd="@dimen/dream_overlay_status_bar_margin"
-        android:paddingStart="@dimen/dream_overlay_status_bar_margin"
-        app:layout_constraintTop_toTopOf="parent">
-
-        <androidx.constraintlayout.widget.ConstraintLayout
-            android:id="@+id/dream_overlay_system_status"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            app:layout_constraintEnd_toEndOf="parent">
-
-            <com.android.systemui.statusbar.AlphaOptimizedImageView
-                android:id="@+id/dream_overlay_wifi_status"
-                android:layout_width="@dimen/status_bar_wifi_signal_size"
-                android:layout_height="match_parent"
-                android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
-                android:visibility="gone"
-                app:layout_constraintEnd_toStartOf="@id/dream_overlay_battery" />
-
-            <com.android.systemui.battery.BatteryMeterView
-                android:id="@+id/dream_overlay_battery"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                app:layout_constraintEnd_toEndOf="parent" />
-
-        </androidx.constraintlayout.widget.ConstraintLayout>
-    </com.android.systemui.dreams.DreamOverlayStatusBarView>
+    <include layout="@layout/dream_overlay_status_bar_view" />
 </com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
new file mode 100644
index 0000000..813787e
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -0,0 +1,96 @@
+<?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.
+  -->
+<com.android.systemui.dreams.DreamOverlayStatusBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/dream_overlay_status_bar"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dream_overlay_status_bar_height"
+    android:paddingEnd="@dimen/dream_overlay_status_bar_margin"
+    android:paddingStart="@dimen/dream_overlay_status_bar_margin"
+    app:layout_constraintTop_toTopOf="parent">
+
+    <com.android.systemui.dreams.DreamOverlayDotImageView
+        android:id="@+id/dream_overlay_notification_indicator"
+        android:layout_width="@dimen/dream_overlay_notification_indicator_size"
+        android:layout_height="@dimen/dream_overlay_notification_indicator_size"
+        android:visibility="gone"
+        android:contentDescription="@string/dream_overlay_status_bar_notification_indicator"
+        app:dotColor="@android:color/white"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent" />
+
+    <LinearLayout
+        android:id="@+id/dream_overlay_system_status"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/dream_overlay_assistant_guest_mode_enabled"
+            android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+            android:src="@drawable/ic_account_circle"
+            android:tint="@android:color/white"
+            android:visibility="gone"
+            android:contentDescription=
+                "@string/dream_overlay_status_bar_assistant_guest_mode_enabled" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/dream_overlay_alarm_set"
+            android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+            android:src="@drawable/ic_alarm"
+            android:tint="@android:color/white"
+            android:visibility="gone"
+            android:contentDescription="@string/dream_overlay_status_bar_alarm_set" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/dream_overlay_priority_mode"
+            android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+            android:src="@drawable/ic_remove_circle"
+            android:tint="@android:color/white"
+            android:visibility="gone"
+            android:contentDescription="@string/dream_overlay_status_bar_priority_mode" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/dream_overlay_wifi_status"
+            android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+            android:src="@drawable/ic_signal_wifi_off"
+            android:visibility="gone"
+            android:contentDescription="@string/dream_overlay_status_bar_wifi_off" />
+
+        <com.android.systemui.dreams.DreamOverlayDotImageView
+            android:id="@+id/dream_overlay_camera_mic_off"
+            android:layout_width="@dimen/dream_overlay_camera_mic_off_indicator_size"
+            android:layout_height="@dimen/dream_overlay_camera_mic_off_indicator_size"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+            android:visibility="gone"
+            android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off"
+            app:dotColor="@color/dream_overlay_camera_mic_off_dot_color" />
+
+    </LinearLayout>
+</com.android.systemui.dreams.DreamOverlayStatusBarView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon.xml b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
deleted file mode 100644
index d4e98e2..0000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon.xml
+++ /dev/null
@@ -1,61 +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.
-  -->
-
-<com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/foreground_service_dungeon"
-    android:layout_width="@dimen/qs_panel_width"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal|bottom"
-    android:visibility="visible"
->
-    <LinearLayout
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:orientation="vertical"
-        android:gravity="bottom"
-        android:visibility="visible"
-        android:background="@drawable/notif_dungeon_bg_gradient"
-    >
-
-        <!-- divider view -->
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="@color/GM2_grey_200"
-            android:visibility="visible"
-        />
-
-        <TextView
-            android:id="@+id/dungeon_title"
-            android:layout_height="48dp"
-            android:layout_width="match_parent"
-            android:padding="8dp"
-            android:text="Apps active in background"
-            android:textColor="@color/GM2_grey_200"
-        />
-
-        <!--  List containing the actual foreground service notifications  -->
-        <LinearLayout
-            android:id="@+id/entry_list"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="bottom"
-            android:orientation="vertical" >
-        </LinearLayout>
-
-    </LinearLayout>
-</com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
deleted file mode 100644
index a6f1638..0000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
+++ /dev/null
@@ -1,43 +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.
-  -->
-
-<com.android.systemui.statusbar.notification.row.DungeonRow
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/foreground_service_dungeon_row"
-    android:layout_width="match_parent"
-    android:layout_height="48dp"
-    android:padding="8dp"
-    android:clickable="true"
-    android:orientation="horizontal" >
-
-    <com.android.systemui.statusbar.StatusBarIconView
-        android:id="@+id/icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:padding="4dp" />
-
-    <TextView
-        android:id="@+id/app_name"
-        android:layout_width="0dp"
-        android:layout_weight="1"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:gravity="center_vertical"
-        android:layout_gravity="center_vertical"
-        android:textColor="@color/GM2_grey_200"
-    />
-
-</com.android.systemui.statusbar.notification.row.DungeonRow>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b7265b9..51211a0 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -86,6 +86,36 @@
     </LinearLayout>
 
     <LinearLayout
+        android:id="@+id/cast_app_section"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:layout_marginStart="@dimen/dialog_side_padding"
+        android:layout_marginEnd="@dimen/dialog_side_padding"
+        android:layout_marginBottom="@dimen/dialog_bottom_padding"
+        android:orientation="vertical"
+        android:visibility="gone">
+        <TextView
+            android:id="@+id/launch_app_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|start"
+            android:ellipsize="end"
+            android:textColor="?android:attr/textColorPrimary"
+            android:text="@string/media_output_dialog_launch_app_text"
+            android:maxLines="1"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textSize="16sp"/>
+
+        <Button
+            android:id="@+id/launch_app_button"
+            style="@style/Widget.Dialog.Button.BorderButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawablePadding="5dp"/>
+    </LinearLayout>
+
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="4dp"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index ab600b3..2567176 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -27,11 +27,11 @@
         android:layout_gravity="center_vertical"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="14sp"
-        android:textColor="@android:color/system_neutral1_900"/>
+        android:textColor="@color/material_dynamic_neutral10"/>
     <TextView
         android:id="@+id/screen_recording_dialog_source_description"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="@android:color/system_neutral2_700"/>
+        android:textColor="@color/material_dynamic_neutral_variant30"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 39d7f4f..c7910afc 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -89,11 +89,6 @@
             layout="@layout/keyguard_status_view"
             android:visibility="gone"/>
 
-        <include layout="@layout/idle_host_view"
-                 android:layout_width="match_parent"
-                 android:layout_height="match_parent"
-                 android:visibility="gone"/>
-
         <include layout="@layout/dock_info_overlay"/>
 
         <FrameLayout
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 2d883bc..6bb6c2d 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -21,9 +21,8 @@
     android:id="@+id/user_switcher_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginBottom="40dp"
-    android:layout_marginEnd="60dp"
-    android:layout_marginStart="60dp">
+    android:layout_marginVertical="40dp"
+    android:layout_marginHorizontal="60dp">
 
   <androidx.constraintlayout.helper.widget.Flow
       android:id="@+id/flow"
@@ -36,7 +35,7 @@
       app:flow_horizontalBias="0.5"
       app:flow_verticalAlign="center"
       app:flow_wrapMode="chain"
-      app:flow_horizontalGap="64dp"
+      app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
       app:flow_verticalGap="44dp"
       app:flow_horizontalStyle="packed"/>
 
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
index 3319442..a3d9a69 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
@@ -21,8 +21,8 @@
   <ImageView
       android:id="@+id/user_switcher_icon"
       android:layout_gravity="center"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content" />
+      android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin"
+      android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" />
   <TextView
       style="@style/Bouncer.UserSwitcher.Spinner.Item"
       android:id="@+id/user_switcher_text"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 42955caf..d8e1dae 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ontkoppel)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan nie wissel nie. Tik om weer te probeer."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string>
     <string name="basic_status" msgid="2315371112182658176">"Maak gesprek oop"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktiewe programme</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiewe program</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuwe inligting"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiewe programme"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Gestop"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Wysig gekopieerde teks"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Wysig gekopieerde prent"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Stuur na toestel in die omtrek"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Voeg by"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Bestuur gebruikers"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 03ae0e4..7138755 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ተቋርጧል)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"መቀየር አይቻልም። እንደገና ለመሞከር መታ ያድርጉ።"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string>
     <string name="basic_status" msgid="2315371112182658176">"ውይይት ይክፈቱ"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ገቢር መተግበሪያዎች</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ገቢር መተግበሪያዎች</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"አዲስ መረጃ"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ገቢር መተግበሪያዎች"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"መቆሚያ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ቆሟል"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"የተቀዳ ጽሁፍ አርትዕ ያድርጉ"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"የተቀዳ ምስል አርትዕ ያድርጉ"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"በአቅራቢያ ወዳለ መሳሪያ ይላኩ"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"አክል"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ተጠቃሚዎችን ያስተዳድሩ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 38dcb89..46e9251e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -830,6 +830,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(غير متّصل)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"لا يمكن التبديل. انقر لإعادة المحاولة."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string>
     <string name="basic_status" msgid="2315371112182658176">"محادثة مفتوحة"</string>
@@ -905,8 +909,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> تطبيق نشط</item>
       <item quantity="one">تطبيق واحد (<xliff:g id="COUNT_0">%s</xliff:g>) نشط</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"معلومات جديدة"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"# تطبيق نشط"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"إيقاف"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"متوقّف"</string>
@@ -914,14 +917,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"تم النسخ."</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"من <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"إغلاق واجهة مستخدم النسخ"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"تعديل النص المنسوخ"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"تعديل الصورة المنسوخة"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"الإرسال إلى جهاز مجاور"</string>
+    <string name="add" msgid="81036585205287996">"إضافة"</string>
+    <string name="manage_users" msgid="1823875311934643849">"إدارة المستخدمين"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 8d8a0fb..beb91b7 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(সংযোগ বিচ্ছিন্ন কৰা হৈছে)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"সলনি কৰিব নোৱাৰি। আকৌ চেষ্টা কৰিবলৈ টিপক।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string>
     <string name="basic_status" msgid="2315371112182658176">"বাৰ্তালাপ খোলক"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> টা সক্ৰিয় এপ্‌</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> টা সক্ৰিয় এপ্‌</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"নতুন তথ্য"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"সক্ৰিয় এপ্‌"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ কৰক"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"বন্ধ হ’ল"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"প্ৰতিলিপি কৰা পাঠ সম্পাদনা কৰক"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"প্ৰতিলিপি কৰা প্ৰতিচ্ছবি সম্পাদনা কৰক"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"নিকটৱৰ্তী ডিভাইচলৈ পঠাওক"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"যোগ দিয়ক"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ব্যৱহাৰকাৰী পৰিচালনা কৰক"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 1fc28a8..97c3c76 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(bağlantı kəsildi)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Dəyişmək olmur. Yenidən cəhd etmək üçün toxunun."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Cihaz əlavə edin"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Montaj nömrəsi"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string>
     <string name="basic_status" msgid="2315371112182658176">"Açıq söhbət"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktiv tətbiq</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiv tətbiq</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Yeni məlumat"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiv tətbiqlər"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dayandırın"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Dayandırılıb"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopyalandı"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Mənbə: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"UI kopyalanmasını qapadın"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Kopyalanmış mətni redaktə edin"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Kopyalanmış şəkli redaktə edin"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yaxınlıqdakı cihaza göndərin"</string>
+    <string name="add" msgid="81036585205287996">"Əlavə edin"</string>
+    <string name="manage_users" msgid="1823875311934643849">"İstifadəçiləri idarə edin"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 555b477..5ac201b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Prebacivanje nije uspelo. Probajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvorite konverzaciju"</string>
@@ -884,8 +888,7 @@
       <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aktivne aplikacije</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktivnih aplikacija</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string>
@@ -893,14 +896,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopirano je"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Iz: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Odbaci kopiranje korisničkog interfejsa"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Izmenite kopirani tekst"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Izmenite kopiranu sliku"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Pošalji na uređaj u blizini"</string>
+    <string name="add" msgid="81036585205287996">"Dodaj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Upravljajte korisnicima"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7446007..b87f9f9 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(адключана)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не ўдалося пераключыцца. Дакраніцеся, каб паўтарыць спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string>
     <string name="basic_status" msgid="2315371112182658176">"Адкрытая размова"</string>
@@ -891,8 +895,7 @@
       <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> актыўных праграм</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> актыўнай праграмы</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Новая інфармацыя"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Актыўныя праграмы"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спыніць"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Спынена"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Скапіравана"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"З праграмы \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Закрыць інтэрфейс капіравання"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Змяніць скапіраваны тэкст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Змяніць скапіраваны відарыс"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Адправіць на прыладу паблізу"</string>
+    <string name="add" msgid="81036585205287996">"Дадаць"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Кіраванне карыстальнікамі"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index f7a1ad6..3d0fee9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(връзката е прекратена)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не може да се превключи. Докоснете за нов опит."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string>
     <string name="basic_status" msgid="2315371112182658176">"Отворен разговор"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> активни приложения</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> активно приложение</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нова информация"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни приложения"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спиране"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Спряно"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Копирано"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"От <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Отхвърляне на ПИ за копиране"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Редактиране на копирания текст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Редактиране на копираното изображение"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Изпращане до устройство в близост"</string>
+    <string name="add" msgid="81036585205287996">"Добавяне"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Управление на потребителите"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 36a5bc4..3cf36e6 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ডিসকানেক্ট হয়ে গেছে)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"পাল্টানো যাচ্ছে না। আবার চেষ্টা করতে ট্যাপ করুন।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string>
     <string name="basic_status" msgid="2315371112182658176">"খোলা কথোপকথন"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g>টি অ্যাক্টিভ অ্যাপ</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g>টি অ্যাক্টিভ অ্যাপ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"নতুন তথ্য"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"অ্যাক্টিভ অ্যাপ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ করুন"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"থামানো হয়েছে"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"কপি করা টেক্সট এডিট করুন"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"কপি করা ছবি এডিট করুন"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"আশেপাশের ডিভাইসে পাঠান"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"যোগ করুন"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ব্যবহারকারীদের ম্যানেজ করুন"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 1545c43..b432204 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -692,7 +692,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikacije koje rade u pozadini"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti prijenos podataka na mobilnoj mreži?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi mreže."</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi-ja."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš operater"</string>
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nije moguće prebaciti. Dodirnite da pokušate ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string>
@@ -884,8 +888,7 @@
       <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aktivne aplikacije</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktivnih aplikacija</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string>
@@ -896,8 +899,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Uredi kopirani tekst"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Uredi kopiranu sliku"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Pošalji na uređaj u blizini"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Dodaj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Upravljajte korisnicima"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 621f5f9..d13b504 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconnectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No es pot canviar. Torna-ho a provar."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa oberta"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicacions actives</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicació activa</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informació nova"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicacions actives"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Atura"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Aturada"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"S\'ha copiat"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"De: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ignora la IU de còpia"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edita el text que has copiat"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edita la imatge que has copiat"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Envia a un dispositiu proper"</string>
+    <string name="add" msgid="81036585205287996">"Afegeix"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gestiona els usuaris"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ba97688..74720a3 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(odpojeno)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nelze přepnout. Klepnutím opakujte akci."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otevřít konverzaci"</string>
@@ -891,8 +895,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktivních aplikací</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktivních aplikací</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Z aplikace <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Zavřít uživatelské rozhraní kopírování"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Upravit zkopírovaný text"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Upravit zkopírovaný obrázek"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Odeslat do zařízení v okolí"</string>
+    <string name="add" msgid="81036585205287996">"Přidat"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Správa uživatelů"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index bbcb9bc..59fd985 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(afbrudt)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Det var ikke muligt at skifte. Tryk for at prøve igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string>
     <string name="basic_status" msgid="2315371112182658176">"Åben samtale"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aktiv app</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktive apps</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nye oplysninger"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stoppet"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopieret"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Fra <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Luk brugerfladen for kopi"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Rediger kopieret tekst"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Rediger kopieret billede"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send til enhed i nærheden"</string>
+    <string name="add" msgid="81036585205287996">"Tilføj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Administrer brugere"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 2b9f725..d6ec9a9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(nicht verbunden)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Wechseln nicht möglich. Tippe, um es noch einmal zu versuchen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string>
     <string name="basic_status" msgid="2315371112182658176">"Offene Unterhaltung"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktive Apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktive App</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Neue Informationen"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive Apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Beenden"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Beendet"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopiert"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Von <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Kopieren-Benutzeroberfläche schließen"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Kopierten Text bearbeiten"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Kopiertes Bild bearbeiten"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"An Gerät in der Nähe senden"</string>
+    <string name="add" msgid="81036585205287996">"Hinzufügen"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Nutzer verwalten"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6096749..b293bbf 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(αποσυνδέθηκε)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Δεν είναι δυνατή η εναλλαγή. Πατήστε για επανάληψη."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string>
     <string name="basic_status" msgid="2315371112182658176">"Άνοιγμα συνομιλίας"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ενεργές εφαρμογές</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ενεργή εφαρμογή</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Νέες πληροφορίες"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ενεργές εφαρμογές"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Διακοπή"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Διακόπηκε"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Αντιγράφηκε"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Από <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Παράβλεψη διεπαφής χρήστη αντιγραφής"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Επεξεργασία αντιγραμμένου κειμένου"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Επεξεργασία αντιγραμμένης εικόνας"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Αποστολή σε κοντινή συσκευή"</string>
+    <string name="add" msgid="81036585205287996">"Προσθήκη"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Διαχείριση χρηστών"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f6dc314..8d6a48d 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> active apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> active app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stopped"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit copied text"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit copied image"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send to nearby device"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Add"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 0b4e009..165db2f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> active apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> active app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stopped"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit copied text"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit copied image"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send to nearby device"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Add"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f6dc314..8d6a48d 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> active apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> active app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stopped"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit copied text"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit copied image"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send to nearby device"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Add"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f6dc314..8d6a48d 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Can\'t switch. Tap to try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> active apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> active app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stopped"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit copied text"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit copied image"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send to nearby device"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Add"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 18d87f3..690a0af 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -806,6 +806,8 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎(disconnected)‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎Can\'t switch. Tap to try again.‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎Pair new device‎‏‎‎‏‎"</string>
+    <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎To cast this session, please open the app.‎‏‎‎‏‎"</string>
+    <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎Unknown app‎‏‎‎‏‎"</string>
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎Build number‎‏‎‎‏‎"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎Build number copied to clipboard.‎‏‎‎‏‎"</string>
     <string name="basic_status" msgid="2315371112182658176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎Open conversation‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 3f1cc42..7df76de 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No se pudo conectar. Presiona para volver a intentarlo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps activas</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app activa</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nueva información"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps activas"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Detenida"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Se copió"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Descartar la copia de la IU"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar el texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar la imagen copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar a dispositivos cercanos"</string>
+    <string name="add" msgid="81036585205287996">"Agregar"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Administrar usuarios"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b84ed65..14484a5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"No se puede cambiar. Toca para volver a intentarlo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Emparejar nuevo dispositivo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicaciones activas</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicación activa</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Información nueva"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicaciones activas"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Detenida"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Cerrar la interfaz de copia"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagen copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar a dispositivo cercano"</string>
+    <string name="add" msgid="81036585205287996">"Añadir"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gestionar usuarios"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 91250c3..81ef384 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ühendus on katkestatud)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ei saa lülitada. Puudutage uuesti proovimiseks."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string>
     <string name="basic_status" msgid="2315371112182658176">"Avage vestlus"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktiivset rakendust</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiivne rakendus</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Uus teave"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiivsed rakendused"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Peata"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Peatatud"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopeeritud"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Rakendusest <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Koopiast loobumise kasutajaliides"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Muuda kopeeritud teksti"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Muuda kopeeritud pilti"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Saada läheduses olevasse seadmesse"</string>
+    <string name="add" msgid="81036585205287996">"Lisa"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Kasutajate haldamine"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b531794..b7d52cb 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deskonektatuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ezin da aldatu. Berriro saiatzeko, sakatu hau."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
     <string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikazio aktibo</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikazio aktibo</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informazio berria"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktibo dauden aplikazioak"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Gelditu"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Geldituta"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopiatu da"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Jatorria: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Kopiatutako UIa baztertzeko botoia"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editatu kopiatutako testua"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editatu kopiatutako irudia"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Bidali inguruko gailu batera"</string>
+    <string name="add" msgid="81036585205287996">"Gehitu"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Kudeatu erabiltzaileak"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 642a826..9fb9626 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(اتصال قطع شد)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"عوض نمی‌شود. برای تلاش مجدد ضربه بزنید."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریده‌دان کپی شد."</string>
     <string name="basic_status" msgid="2315371112182658176">"باز کردن مکالمه"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> برنامه فعال</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> برنامه فعال</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"اطلاعات جدید"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"برنامه‌های فعال"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"توقف"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"متوقف شد"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"کپی شد"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"از <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"رد کردن رابط کاربری کپی کردن"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"ویرایش نوشتار کپی‌شده"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"ویرایش تصویر کپی‌شده"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ارسال به دستگاهی در اطراف"</string>
+    <string name="add" msgid="81036585205287996">"افزودن"</string>
+    <string name="manage_users" msgid="1823875311934643849">"مدیریت کاربران"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index a9c11bc..eec7e46 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(yhteys katkaistu)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Vaihtaminen ei onnistunut. Yritä uudelleen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string>
     <string name="basic_status" msgid="2315371112182658176">"Avaa keskustelu"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktiivista sovellusta</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiivinen sovellus</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Uutta tietoa"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiiviset sovellukset"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Lopeta"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Pysäytetty"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopioitu"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Lähde: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Hylkää kopioitu UI"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Muokkaa kopioitua tekstiä"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Muokkaa kopioitua kuvaa"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Lähetä lähellä olevaan laitteeseen"</string>
+    <string name="add" msgid="81036585205287996">"Lisää"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Ylläpidä käyttäjiä"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 3ee8efa..0ec3fdf 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Changement impossible. Touchez pour réessayer."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ouvrir la conversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> application active</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> applications actives</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"À partir de <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ignorer la copie de l\'IU"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifier le texte copié"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifier l\'image copiée"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Envoyer à un appareil à proximité"</string>
+    <string name="add" msgid="81036585205287996">"Ajouter"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gérer les utilisateurs"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 0af35ba..cbbee60 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Impossible de changer. Appuyez pour réessayer."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversation ouverte"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> appli active</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> applis actives</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelles informations"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Désactiver l\'interface de copie"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifier le texte copié"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifier l\'image copiée"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Envoyer à un appareil à proximité"</string>
+    <string name="add" msgid="81036585205287996">"Ajouter"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gérer les utilisateurs"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 81efcb5..cc35925 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Non se puido realizar o cambio. Toca para tentalo de novo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicacións activas</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicación activa</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova información"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicacións activas"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Deter"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Detida"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiouse"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ignorar interface de copia"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imaxe copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar a dispositivo próximo"</string>
+    <string name="add" msgid="81036585205287996">"Engadir"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Xestionar usuarios"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 98510d5..90618a6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ડિસ્કનેક્ટ કરેલું)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"સ્વિચ કરી શકતા નથી. ફરી પ્રયાસ કરવા માટે ટૅપ કરો."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string>
     <string name="basic_status" msgid="2315371112182658176">"વાતચીત ખોલો"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> સક્રિય ઍપ</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> સક્રિય ઍપ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"નવી માહિતી"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"સક્રિય ઍપ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"રોકો"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"બંધ કરેલી છે"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"કૉપિ કરવામાં આવી"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g>માંથી"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"\'UI | યૂઝર ઇન્ટરફેસ (UI) કૉપિ કરો\'ને છોડી દો"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"કૉપિ કરેલી ટેક્સ્ટમાં ફેરફાર કરો"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"કૉપિ કરેલી છબીમાં ફેરફાર કરો"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"નજીકના ડિવાઇસને મોકલો"</string>
+    <string name="add" msgid="81036585205287996">"ઉમેરો"</string>
+    <string name="manage_users" msgid="1823875311934643849">"વપરાશકર્તાઓને મેનેજ કરો"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 2631f55..d47c71a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string>
     <string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ऐप्लिकेशन चालू है</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ऐप्लिकेशन चालू हैं</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"नई जानकारी"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ये ऐप्लिकेशन चालू हैं"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"बंद करें"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"रुका हुआ है"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"कॉपी किया गया"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> से"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"कॉपी किया गया यूज़र इंटरफ़ेस (यूआई) खारिज करें"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"कॉपी किए गए टेक्स्ट में बदलाव करें"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"कॉपी की गई इमेज में बदलाव करें"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"कॉन्टेंट को आस-पास मौजूद डिवाइस पर भेजें"</string>
+    <string name="add" msgid="81036585205287996">"जोड़ें"</string>
+    <string name="manage_users" msgid="1823875311934643849">"उपयोगकर्ताओं को मैनेज करें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index da84b86..64fdabb 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(nije povezano)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nije prebačeno. Dodirnite da biste pokušali ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string>
@@ -884,8 +888,7 @@
       <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aktivne aplikacije</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktivnih aplikacija</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string>
@@ -893,14 +896,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopirano"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Iz aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Odbaci kopiranje korisničkog sučelja"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Uredi kopirani tekst"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Uredi kopiranu sliku"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Pošalji uređaju u blizini"</string>
+    <string name="add" msgid="81036585205287996">"Dodaj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Upravljanje korisnicima"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 30a5be8..6589e71 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(leválasztva)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"A váltás nem sikerült. Próbálja újra."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string>
     <string name="basic_status" msgid="2315371112182658176">"Beszélgetés megnyitása"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktív alkalmazás</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktív alkalmazás</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Új információ"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktív alkalmazások"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Leállítás"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Leállítva"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Vágólapra másolt szöveg szerkesztése"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Vágólapra másolt kép szerkesztése"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Küldés közeli eszközre"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Hozzáadás"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Felhasználók kezelése"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 443da54..fe0e091 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(անջատված է)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Սխալ առաջացավ։ Հպեք՝ կրկնելու համար։"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string>
     <string name="basic_status" msgid="2315371112182658176">"Բաց զրույց"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ակտիվ հավելված</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ակտիվ հավելված</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Նոր տեղեկություն"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ակտիվ հավելվածներ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Դադարեցնել"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Կանգնեցված է"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Պատճենվեց"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածից"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Փակել պատճենների միջերեսը"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Փոփոխել պատճենված տեքստը"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Փոփոխել պատճենված պատկերը"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Ուղարկել մոտակա սարքի"</string>
+    <string name="add" msgid="81036585205287996">"Ավելացնել"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Օգտատերերի կառավարում"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c7339c7..cf4e684 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(terputus)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Tidak dapat beralih. Ketuk untuk mencoba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string>
     <string name="basic_status" msgid="2315371112182658176">"Membuka percakapan"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikasi aktif</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikasi aktif</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informasi baru"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplikasi aktif"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Dihentikan"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Disalin"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Dari <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Tutup UI salin"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit teks yang disalin"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit gambar yang disalin"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Kirim ke perangkat di sekitar"</string>
+    <string name="add" msgid="81036585205287996">"Tambahkan"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Kelola pengguna"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index eddd246..0ac6c20 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(aftengt)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ekki er hægt að skipta. Ýttu til að reyna aftur."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string>
     <string name="basic_status" msgid="2315371112182658176">"Opna samtal"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> virkt forrit</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> virk forrit</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nýjar upplýsingar"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Virk forrit"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stöðva"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stöðvað"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Afritað"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Frá <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Loka afriti notendaviðmóts"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Breyta afrituðum texta"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Breyta afritaðri mynd"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Senda í nálægt tæki"</string>
+    <string name="add" msgid="81036585205287996">"Bæta við"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Stjórna notendum"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 6a07f79..f7af9892 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(disconnesso)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Non puoi cambiare. Tocca per riprovare."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string>
     <string name="basic_status" msgid="2315371112182658176">"Apri conversazione"</string>
@@ -877,22 +881,17 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app attiva</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> app attive</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuove informazioni"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"App attive"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Interrompi"</string>
-    <!-- no translation found for fgs_manager_app_item_stop_button_stopped_label (6950382004441263922) -->
-    <skip />
+    <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Interrotta"</string>
     <string name="clipboard_edit_text_copy" msgid="770856373439969178">"Copia"</string>
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiato"</string>
-    <!-- no translation found for clipboard_edit_source (9156488177277788029) -->
-    <skip />
+    <string name="clipboard_edit_source" msgid="9156488177277788029">"Da <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ignora copia UI"</string>
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifica testo copiato"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifica immagine copiata"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Invia a dispositivo nelle vicinanze"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Aggiungi"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gestisci utenti"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f959774..df47c1c 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(מנותק)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"לא ניתן להחליף. צריך להקיש כדי לנסות שוב."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"‏מספר Build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"‏מספר ה-Build הועתק ללוח."</string>
     <string name="basic_status" msgid="2315371112182658176">"פתיחת שיחה"</string>
@@ -891,8 +895,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> אפליקציות פעילות</item>
       <item quantity="one">אפליקציה פעילה אחת (<xliff:g id="COUNT_0">%s</xliff:g>)</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"מידע חדש"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"אפליקציות פעילות"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"עצירה"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"הופסקה"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"הועתק"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"המקור: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"ביטול של העתקת ממשק המשתמש"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"עריכת הטקסט שהועתק"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"עריכת התמונה שהועתקה"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"שליחה למכשיר בקרבת מקום"</string>
+    <string name="add" msgid="81036585205287996">"הוספה"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ניהול משתמשים"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 05ddace..f0353b9 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(接続解除済み)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"切り替えられません。タップしてやり直してください。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string>
     <string name="basic_status" msgid="2315371112182658176">"空の会話"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">有効なアプリ: <xliff:g id="COUNT_1">%s</xliff:g> 個</item>
       <item quantity="one">有効なアプリ: <xliff:g id="COUNT_0">%s</xliff:g> 個</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"最新情報"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"有効なアプリ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"停止中"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"コピーしたテキストを編集"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"コピーした画像を編集"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"付近のデバイスに送信"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"追加"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ユーザーの管理"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index cf77cac..a0ce6be 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(კავშირი გაწყვეტილია)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ვერ გადაირთო. შეეხეთ ხელახლა საცდელად."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string>
     <string name="basic_status" msgid="2315371112182658176">"მიმოწერის გახსნა"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> აქტიური აპი</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> აქტიური აპი</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ახალი ინფორმაცია"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"აქტიური აპები"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"შეწყვეტა"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"შეწყვეტილია"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"კოპირებული ტექსტის რედაქტირება"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"კოპირებული სურათის რედაქტირება"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ახლომახლო მოწყობილობაზე გაგზავნა"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"დამატება"</string>
+    <string name="manage_users" msgid="1823875311934643849">"მომხმარებლების მართვა"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 32d2e51..42c5d04 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ажыратулы)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Ауысу мүмкін емес. Әрекетті қайталау үшін түртіңіз."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғымен жұптау"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ашық әңгіме"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> белсенді қолданба</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> белсенді қолданба</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Жаңа ақпарат"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Белсенді қолданбалар"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Тоқтату"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Тоқтатылған"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Көшірілді"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> қолданбасынан"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Көшіру интерфейсін жабу"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Көшірілген мәтінді өңдеу"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Көшірілген суретті өңдеу"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Маңайдағы құрылғыға жіберу"</string>
+    <string name="add" msgid="81036585205287996">"Қосу"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Пайдаланушыларды басқару"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 581d3e6..72f001f 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(បាន​ដាច់)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"មិនអាចប្ដូរបានទេ។ សូមចុចដើម្បី​ព្យាយាម​ម្ដងទៀត។"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខ​កំណែបង្កើត"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខ​កំណែបង្កើតទៅឃ្លីបបត។"</string>
     <string name="basic_status" msgid="2315371112182658176">"បើកការសន្ទនា"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">កម្មវិធីសកម្ម <xliff:g id="COUNT_1">%s</xliff:g></item>
       <item quantity="one">កម្មវិធីសកម្ម <xliff:g id="COUNT_0">%s</xliff:g></item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ព័ត៌មានថ្មី"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"កម្មវិធីសកម្ម"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ឈប់"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"បានឈប់"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"បានចម្លង"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"ពី <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"ច្រានចោល UI ចម្លង"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"កែអត្ថបទ​ដែលបានចម្លង"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"កែរូបភាព​ដែលបានចម្លង"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ផ្ញើទៅ​ឧបករណ៍​នៅជិត"</string>
+    <string name="add" msgid="81036585205287996">"បញ្ចូល"</string>
+    <string name="manage_users" msgid="1823875311934643849">"គ្រប់គ្រង​អ្នក​ប្រើប្រាស់"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index f1428a0..660c966 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -176,7 +176,7 @@
     <skip />
     <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ಅಧಿಸೂಚನೆಯ ಛಾಯೆ."</string>
     <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳು."</string>
-    <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್‌ ಪರದೆ."</string>
+    <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್‌ ಸ್ಕ್ರೀನ್."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string>
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ಡಿಸ್‌ಕನೆಕ್ಟ್ ಆಗಿದೆ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ಬದಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string>
     <string name="basic_status" msgid="2315371112182658176">"ಸಂಭಾಷಣೆಯನ್ನು ತೆರೆಯಿರಿ"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ಸಕ್ರಿಯ ಆ್ಯಪ್‌ಗಳು</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ಸಕ್ರಿಯ ಆ್ಯಪ್‌ಗಳು</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ಹೊಸ ಮಾಹಿತಿ"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ಸಕ್ರಿಯ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ನಿಲ್ಲಿಸಿ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ನಿಲ್ಲಿಸಿದೆ"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"ನಕಲಿಸಿದ ಪಠ್ಯವನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"ನಕಲಿಸಿದ ಚಿತ್ರವನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಕ್ಕೆ ಕಳುಹಿಸಿ"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"ಸೇರಿಸಿ"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 02246b4..3d60264 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(연결 끊김)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"전환할 수 없습니다. 다시 시도하려면 탭하세요."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string>
     <string name="basic_status" msgid="2315371112182658176">"대화 열기"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">활성 상태의 앱 <xliff:g id="COUNT_1">%s</xliff:g>개</item>
       <item quantity="one">활성 상태의 앱 <xliff:g id="COUNT_0">%s</xliff:g>개</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"새로운 정보"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"활성 상태의 앱"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"중지"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"중지됨"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"복사됨"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"복사한 위치: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"UI 복사 닫기"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"복사된 텍스트 편집"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"복사된 이미지 편집"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"근처 기기에 전송"</string>
+    <string name="add" msgid="81036585205287996">"추가"</string>
+    <string name="manage_users" msgid="1823875311934643849">"사용자 관리"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index c8c56ca..f16736d 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ажыратылды)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Которулбай жатат. Кайталоо үчүн басыңыз."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөктү жупташтыруу"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string>
     <string name="basic_status" msgid="2315371112182658176">"Ачык сүйлөшүү"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> жигердүү колдонмо</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> жигердүү колдонмо</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Жаңы маалымат"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Жигердүү колдонмолор"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Токтотуу"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Токтотулду"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Көчүрүлдү"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> колдонмосунан"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Көчүрмөнү жабуу интерфейси"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Көчүрүлгөн текстти түзөтүү"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Көчүрүлгөн сүрөттү түзөтүү"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Жакын жердеги түзмөккө жөнөтүү"</string>
+    <string name="add" msgid="81036585205287996">"Кошуу"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Колдонуучуларды башкаруу"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 6b96c1a..6be5b7ca 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ຕັດການເຊື່ອມຕໍ່ແລ້ວ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ບໍ່ສາມາດສະຫຼັບໄດ້. ແຕະເພື່ອລອງໃໝ່."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string>
     <string name="basic_status" msgid="2315371112182658176">"ເປີດການສົນທະນາ"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">ແອັບທີ່ນຳໃຊ້ຢູ່ <xliff:g id="COUNT_1">%s</xliff:g> ແອັບ</item>
       <item quantity="one">ແອັບທີ່ນຳໃຊ້ຢູ່ <xliff:g id="COUNT_0">%s</xliff:g> ແອັບ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ຂໍ້ມູນໃໝ່"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ແອັບທີ່ນຳໃຊ້ຢູ່"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ຢຸດ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ຢຸດແລ້ວ"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"ແກ້ໄຂຂໍ້ຄວາມທີ່ສຳເນົາແລ້ວ"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"ແກ້ໄຂຮູບທີ່ສຳເນົາແລ້ວ"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ສົ່ງໄປຫາອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"ເພີ່ມ"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ຈັດການຜູ້ໃຊ້"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5179ad9..b0878c7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(atjungta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nepavyko perjungti. Bandykite vėl palietę."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijos numeris"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas į iškarpinę."</string>
     <string name="basic_status" msgid="2315371112182658176">"Atidaryti pokalbį"</string>
@@ -891,8 +895,7 @@
       <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> aktyvios programos</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktyvių programų</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nauja informacija"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktyvios programos"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Sustabdyti"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Sustabdyta"</string>
@@ -903,8 +906,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Redaguoti nukopijuotą tekstą"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Redaguoti nukopijuotą vaizdą"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Siųsti į įrenginį netoliese"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Pridėti"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Tvarkyti naudotojus"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 63b6b2e..c32a047 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(savienojums pārtraukts)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nevar pārslēgt. Pieskarieties, lai mēģinātu vēlreiz."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string>
     <string name="basic_status" msgid="2315371112182658176">"Atvērt sarunu"</string>
@@ -884,8 +888,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aktīva lietotne</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktīvas lietotnes</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Jauna informācija"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktīvās lietotnes"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Apturēt"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Apturēta"</string>
@@ -893,14 +896,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nokopēts"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"No lietotnes <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Noraidīt ar kopēšanu saistīto lietotāja saskarnes elementu"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Rediģēt nokopēto tekstu"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Rediģēt nokopēto attēlu"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Sūtīt uz tuvumā esošu ierīci"</string>
+    <string name="add" msgid="81036585205287996">"Pievienot"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Pārvaldīt lietotājus"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 7642a9a..c8799da 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(врската е прекината)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не се префрла. Допрете и обидете се пак."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string>
     <string name="basic_status" msgid="2315371112182658176">"Започни разговор"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> активна апликација</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> активни апликации</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Крај"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Копирано"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Од <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Отфрли го корисничкиот интерфејс за копирање"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Изменете го копираниот текст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Изменете ја копираната слика"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Испратете до уред во близина"</string>
+    <string name="add" msgid="81036585205287996">"Додај"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Управувајте со корисниците"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 1bde5a0..2359ae6 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(വിച്ഛേദിച്ചു)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"മാറാനാകുന്നില്ല. വീണ്ടും ശ്രമിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string>
     <string name="basic_status" msgid="2315371112182658176">"സംഭാഷണം തുറക്കുക"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">സജീവമായ <xliff:g id="COUNT_1">%s</xliff:g> ആപ്പുകൾ</item>
       <item quantity="one">സജീവമായ <xliff:g id="COUNT_0">%s</xliff:g> ആപ്പ്</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"പുതിയ വിവരങ്ങൾ"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"സജീവമായ ആപ്പുകൾ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"നിർത്തുക"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"നിർത്തി"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"പകർത്തിയ ടെക്സ്റ്റ് എഡിറ്റ് ചെയ്യുക"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"പകർത്തിയ ചിത്രം എഡിറ്റ് ചെയ്യുക"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"സമീപത്തുള്ള ഉപകരണത്തിലേക്ക് അയയ്ക്കുക"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"ചേർക്കുക"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ഉപയോക്താക്കളെ മാനേജ് ചെയ്യുക"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index c63bd3d..c090279 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(салсан)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Сэлгэх боломжгүй. Дахин оролдохын тулд товшино уу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийцийн дугаар"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Хийцийн дугаарыг түр санах ойд хуулсан."</string>
     <string name="basic_status" msgid="2315371112182658176">"Харилцан яриаг нээх"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">Идэвхтэй <xliff:g id="COUNT_1">%s</xliff:g> апп</item>
       <item quantity="one">Идэвхтэй <xliff:g id="COUNT_0">%s</xliff:g> апп</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Шинэ мэдээлэл"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Идэвхтэй аппууд"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зогсоох"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Зогсоосон"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Хуулсан текстийг засах"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Хуулсан зургийг засах"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Ойролцоох төхөөрөмж рүү илгээх"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Нэмэх"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Хэрэглэгчдийг удирдах"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index fca67d2..eb3b2eb 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिस्कनेक्ट केलेले)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच करू शकत नाही. पुन्हा प्रयत्न करण्यासाठी टॅप करा."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string>
     <string name="basic_status" msgid="2315371112182658176">"संभाषण उघडा"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> अ‍ॅक्टिव्ह ॲप्स</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> अ‍ॅक्टिव्ह ॲप</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"नवीन माहिती"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"अ‍ॅक्टिव्ह ॲप्स"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"थांबवा"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"थांबवले"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"कॉपी केलेला मजकूर संपादित करा"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"कॉपी केलेली इमेज संपादित करा"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"जवळपासच्या डिव्हाइसवर पाठवा"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"जोडा"</string>
+    <string name="manage_users" msgid="1823875311934643849">"वापरकर्ते व्यवस्‍थापित करा"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b87a79c..656080e 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(diputuskan sambungan)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Tidak dapat menukar. Ketik untuk mencuba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string>
     <string name="basic_status" msgid="2315371112182658176">"Buka perbualan"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apl aktif</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> apl aktif</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Maklumat baharu"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apl aktif"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Dihentikan"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Disalin"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Daripada <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ketepikan penyalinan UI"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edit teks yang disalin"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edit imej yang disalin"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Hantar ke peranti berdekatan"</string>
+    <string name="add" msgid="81036585205287996">"Tambah"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Urus pengguna"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 0248d10..a72293c 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ချိတ်ဆက်မှု မရှိပါ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ပြောင်း၍ မရပါ။ ပြန်စမ်းကြည့်ရန် တို့ပါ။"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string>
     <string name="basic_status" msgid="2315371112182658176">"စကားဝိုင်းကို ဖွင့်ရန်"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">ပွင့်နေသည့်အက်ပ် <xliff:g id="COUNT_1">%s</xliff:g> ခု</item>
       <item quantity="one">ပွင့်နေသည့်အက်ပ် <xliff:g id="COUNT_0">%s</xliff:g> ခု</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"အချက်အလက်သစ်"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ပွင့်နေသည့်အက်ပ်များ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ရပ်ရန်"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ရပ်ထားသည်"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"ကူးပြီးပါပြီ"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> ထံမှ"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"UI မိတ္တူမကူးတော့ရန်"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"ကူးထားသည့်စာသားကို တည်းဖြတ်ရန်"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"ကူးထားသည့်ပုံကို ပြင်ဆင်ရန်"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"အနီးတစ်ဝိုက်ရှိ စက်များသို့ ပို့ရန်"</string>
+    <string name="add" msgid="81036585205287996">"ထည့်ရန်"</string>
+    <string name="manage_users" msgid="1823875311934643849">"အသုံးပြုသူများ စီမံရန်"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 76cfef5..45c0f90 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(frakoblet)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan ikke bytte. Trykk for å prøve igjen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string>
     <string name="basic_status" msgid="2315371112182658176">"Åpen samtale"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktive apper</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiv app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Ny informasjon"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive apper"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stopp"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stoppet"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopiert"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Fra <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Lukk kopi-UI"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Rediger den kopierte teksten"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Rediger det kopierte bildet"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Send til en enhet i nærheten"</string>
+    <string name="add" msgid="81036585205287996">"Legg til"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Administrer brukere"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 158a891..938351f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिस्कनेक्ट गरिएको छ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"बदल्न सकिएन। फेरि प्रयास गर्न ट्याप गर्नुहोस्।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string>
     <string name="basic_status" msgid="2315371112182658176">"वार्तालाप खोल्नुहोस्"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> वटा सक्रिय एप</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> सक्रिय एप</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"नयाँ जानकारी"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"सक्रिय एपहरू"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"रोक्नुहोस्"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"रोकिएको छ"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"कपी गरिएको टेक्स्ट सम्पादन गर्नुहोस्"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"कपी गरिएको फोटो सम्पादन गर्नुहोस्"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"नजिकैको डिभाइसमा पठाउनुहोस्"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"हाल्नुहोस्"</string>
+    <string name="manage_users" msgid="1823875311934643849">"प्रयोगकर्ताहरूको व्यवस्थापन गर्नुहोस्"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index b318bbc..4b96d5d 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,7 +33,7 @@
     <!-- The color of the text inside a notification -->
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
 
-    <color name="notif_pill_text">@android:color/system_neutral1_50</color>
+    <color name="notif_pill_text">@color/material_dynamic_neutral95</color>
 
     <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
     <color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
@@ -66,15 +66,15 @@
     <color name="media_divider">#85ffffff</color>
 
     <!-- media output dialog-->
-    <color name="media_dialog_background">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_active_item_main_content">@android:color/system_accent2_800</color>
-    <color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_100</color>
-    <color name="media_dialog_item_status">@android:color/system_accent1_100</color>
-    <color name="media_dialog_item_background">@android:color/system_neutral2_800</color>
+    <color name="media_dialog_background">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_gray">#ffcccccc</color>
-    <color name="biometric_dialog_accent">@android:color/system_accent1_300</color>
+    <color name="biometric_dialog_accent">@color/material_dynamic_primary70</color>
     <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
 
     <!-- UDFPS colors -->
@@ -99,5 +99,5 @@
     <!-- Accessibility floating menu -->
     <color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
 
-    <color name="people_tile_background">@android:color/system_accent2_800</color>
+    <color name="people_tile_background">@color/material_dynamic_secondary20</color>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index f85bbee..67eee11 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(verbinding verbroken)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Kan niet schakelen. Tik om het opnieuw te proberen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-nummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Build-nummer naar klembord gekopieerd."</string>
     <string name="basic_status" msgid="2315371112182658176">"Gesprek openen"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> actieve apps</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> actieve app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nieuwe informatie"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Actieve apps"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppen"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Gestopt"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Gekopieerd"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Uit <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"UI voor kopiëren sluiten"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Gekopieerde tekst bewerken"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Gekopieerde afbeelding bewerken"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Naar apparaat in de buurt sturen"</string>
+    <string name="add" msgid="81036585205287996">"Toevoegen"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gebruikers beheren"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index dab199b..57cdf0b 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ସ୍ୱିଚ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string>
     <string name="basic_status" msgid="2315371112182658176">"ବାର୍ତ୍ତାଳାପ ଖୋଲନ୍ତୁ"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g>ଟି ସକ୍ରିୟ ଆପ</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g>ଟି ସକ୍ରିୟ ଆପ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପଗୁଡ଼ିକ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"କପି କରାଯାଇଥିବା ଟେକ୍ସଟକୁ ଏଡିଟ କରନ୍ତୁ"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"କପି କରାଯାଇଥିବା ଇମେଜକୁ ଏଡିଟ କରନ୍ତୁ"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ନିକଟସ୍ଥ ଡିଭାଇସକୁ ପଠାନ୍ତୁ"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"ଯୋଗ କରନ୍ତୁ"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 20b5bf2..63eb3fb 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ਡਿਸਕਨੈਕਟ ਹੈ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="basic_status" msgid="2315371112182658176">"ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ਕਿਰਿਆਸ਼ੀਲ ਐਪ</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ਕਿਰਿਆਸ਼ੀਲ ਐਪਾਂ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ਨਵੀਂ ਜਾਣਕਾਰੀ"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ਕਿਰਿਆਸ਼ੀਲ ਐਪਾਂ"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ਬੰਦ ਕਰੋ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ਬੰਦ ਹੈ"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"ਕਾਪੀ ਕੀਤੀ ਗਈ"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤੋਂ"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"ਕਾਪੀ ਕੀਤੇ UI ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"ਕਾਪੀ ਕੀਤੀ ਲਿਖਤ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"ਕਾਪੀ ਕੀਤੇ ਗਏ ਚਿੱਤਰ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ \'ਤੇ ਭੇਜੋ"</string>
+    <string name="add" msgid="81036585205287996">"ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="manage_users" msgid="1823875311934643849">"ਵਰਤੋਂਕਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4572922..463f1d8 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(odłączono)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nie można przełączyć. Spróbuj ponownie."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otwarta rozmowa"</string>
@@ -891,8 +895,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktywnej aplikacji</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktywna aplikacja</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nowa informacja"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktywne aplikacje"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zatrzymaj"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zatrzymano"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Skopiowano"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Od: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Zamknij UI kopiowania"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Edytuj skopiowany tekst"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Edytuj skopiowany obraz"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Wyślij na urządzenie w pobliżu"</string>
+    <string name="add" msgid="81036585205287996">"Dodaj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Zarządzaj użytkownikami"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 1c902b0..226d338 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(sem conexão)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Não foi possível mudar. Toque para tentar novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app ativo</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps ativos</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Do app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Dispensar cópia da IU"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
+    <string name="add" msgid="81036585205287996">"Adicionar"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gerenciar usuários"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1562abc..d4cfc83 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(desligado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Não é possível mudar. Toque para tentar novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Abrir conversa"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app ativa</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps ativas</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Novas informações"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativas"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parada"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Da app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ignorar cópia de IU"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
+    <string name="add" msgid="81036585205287996">"Adicionar"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gerir utilizadores"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 1c902b0..226d338 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(sem conexão)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Não foi possível mudar. Toque para tentar novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string>
     <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app ativo</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps ativos</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copiado"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Do app <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Dispensar cópia da IU"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editar texto copiado"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editar imagem copiada"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Enviar para dispositivo próximo"</string>
+    <string name="add" msgid="81036585205287996">"Adicionar"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gerenciar usuários"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d520d50..53524de 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deconectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nu se poate comuta. Atingeți pentru a încerca din nou."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
     <string name="basic_status" msgid="2315371112182658176">"Deschideți conversația"</string>
@@ -884,8 +888,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> de aplicații active</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicație activă</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informații noi"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicații active"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Opriți"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Oprită"</string>
@@ -896,8 +899,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editați textul copiat"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editați imaginea copiată"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Trimiteți către un dispozitiv din apropiere"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Adăugați"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Gestionați utilizatorii"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 310a87e..969ad8a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(нет подключения)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не удается переключиться. Нажмите, чтобы повторить попытку."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string>
     <string name="basic_status" msgid="2315371112182658176">"Открытый чат"</string>
@@ -891,8 +895,7 @@
       <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> активных приложений</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> активного приложения</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Новая информация"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активные приложения"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Остановить"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Остановлено"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Скопировано."</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Из приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Закрыть меню копирования"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Изменить скопированный текст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Изменить скопированное изображение"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Отправить на устройство поблизости"</string>
+    <string name="add" msgid="81036585205287996">"Добавить"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Управление пользователями"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index e730bb0..b944799 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(විසන්ධි විය)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"මාරු කිරීමට නොහැකිය. නැවත උත්සාහ කිරීමට තට්ටු කරන්න."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string>
     <string name="basic_status" msgid="2315371112182658176">"සංවාදය විවෘත කරන්න"</string>
@@ -877,8 +881,7 @@
       <item quantity="one">සක්‍රිය යෙදුම් <xliff:g id="COUNT_1">%s</xliff:g></item>
       <item quantity="other">සක්‍රිය යෙදුම් <xliff:g id="COUNT_1">%s</xliff:g></item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"නව තොරතුරු"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"සක්‍රිය යෙදුම්"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"නවත්වන්න"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"නවත්වන ලදි"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"පිටපත් කරන ලදි"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> සිට"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Dismiss copy UI"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"පිටපත් කළ පෙළ සංස්කරණය කරන්න"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"පිටපත් කළ රූපය සංස්කරණය කරන්න"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"අවට උපාංගය වෙත යවන්න"</string>
+    <string name="add" msgid="81036585205287996">"එක් කරන්න"</string>
+    <string name="manage_users" msgid="1823875311934643849">"පරිශීලකයන් කළමනාකරණය කරන්න"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index a3bbe44..2236c8a 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(odpojené)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nedá sa prepnúť. Zopakujte klepnutím."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string>
     <string name="basic_status" msgid="2315371112182658176">"Otvorená konverzácia"</string>
@@ -891,8 +895,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktívnych aplikácií</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktívna aplikácia</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informácie"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktívne aplikácie"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ukončiť"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastavená"</string>
@@ -903,8 +906,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Upraviť skopírovaný text"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Upraviť skopírovaný obrázok"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Odoslať do zariadenia v okolí"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Pridať"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Spravovať používateľov"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index d657277..46ee48f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(povezava je prekinjena)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Preklop ni mogoč. Če želite poskusiti znova, se dotaknite."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string>
     <string name="basic_status" msgid="2315371112182658176">"Odprt pogovor"</string>
@@ -891,8 +895,7 @@
       <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aktivne aplikacije</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktivnih aplikacij</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ustavi"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Ustavljeno"</string>
@@ -903,8 +906,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Uredi kopirano besedilo"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Uredi kopirano sliko"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Pošlji v napravo v bližini"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Dodaj"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Upravljanje uporabnikov"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f8ba65af..e3d233f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(shkëputur)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nuk mund të ndërrohet. Trokit për të provuar përsëri."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string>
     <string name="basic_status" msgid="2315371112182658176">"Hap bisedën"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacione aktive</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikacion aktiv</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Informacion i ri"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplikacionet aktive"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ndalo"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Ndaluar"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"U kopjua"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Nga <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Hiq kopjen e ndërfaqes së përdoruesit"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifiko tekstin e kopjuar"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifiko imazhin e kopjuar"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Dërgo te pajisja në afërsi"</string>
+    <string name="add" msgid="81036585205287996">"Shto"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Menaxho përdoruesit"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5d89cf1..4c593ab 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -812,6 +812,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(веза је прекинута)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Пребацивање није успело. Пробајте поново."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string>
     <string name="basic_status" msgid="2315371112182658176">"Отворите конверзацију"</string>
@@ -884,8 +888,7 @@
       <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> активне апликације</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> активних апликација</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нове информације"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активне апликације"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Заустави"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Заустављено"</string>
@@ -893,14 +896,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Копирано је"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Из: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Одбаци копирање корисничког интерфејса"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Измените копирани текст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Измените копирану слику"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Пошаљи на уређај у близини"</string>
+    <string name="add" msgid="81036585205287996">"Додај"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Управљаjте корисницима"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b9a3ab1..7686143 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(frånkopplad)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Misslyckat byte. Tryck och försök igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string>
     <string name="basic_status" msgid="2315371112182658176">"Öppen konversation"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aktiva appar</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aktiv app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Ny information"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiva appar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppa"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Stoppad"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopierades"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Från <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Stäng användargränssnittet för kopiering"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Redigera kopierad text"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Redigera kopierad bild"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Skicka till enhet i närheten"</string>
+    <string name="add" msgid="81036585205287996">"Lägg till"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Hantera användare"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 803a2b5..92b6d92 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(imetenganishwa)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Imeshindwa kubadilisha. Gusa ili ujaribu tena."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string>
     <string name="basic_status" msgid="2315371112182658176">"Fungua mazungumzo"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">Programu <xliff:g id="COUNT_1">%s</xliff:g> zinatumika</item>
       <item quantity="one">Programu <xliff:g id="COUNT_0">%s</xliff:g> inatumika</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Maelezo mapya"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Programu zinazotumika"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Simamisha"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Imesimamishwa"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Imenakiliwa"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Kutoka <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Ondoa kiolesura cha nakala"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Badilisha maandishi yaliyonakiliwa"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Badilisha picha iliyonakiliwa"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Tuma kwenye kifaa kilicho karibu"</string>
+    <string name="add" msgid="81036585205287996">"Weka"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Dhibiti watumiaji"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 094559f..4ae3e18 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(துண்டிக்கப்பட்டது)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"இணைக்க முடியவில்லை. மீண்டும் முயல தட்டவும்."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string>
     <string name="basic_status" msgid="2315371112182658176">"திறந்தநிலை உரையாடல்"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ஆப்ஸ் செயலில் உள்ளன</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ஆப்ஸ் செயலில் உள்ளது</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"புதிய தகவல்கள்"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"செயலிலுள்ள ஆப்ஸ்"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"நிறுத்து"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"இயங்கவில்லை"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"நகலெடுத்த வார்த்தைகளைத் திருத்து"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"நகலெடுத்த படத்தைத் திருத்து"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"அருகிலுள்ள சாதனத்திற்கு அனுப்பு"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"சேர்"</string>
+    <string name="manage_users" msgid="1823875311934643849">"பயனர்களை நிர்வகித்தல்"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 59e8ef4..521c63e 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(డిస్కనెక్ట్ అయ్యింది)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"స్విచ్ చేయడం సాధ్యం కాదు. మళ్ళీ ట్రై చేయడానికి ట్యాప్ చేయండి."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్‌బోర్డ్‌కు కాపీ చేయబడింది."</string>
     <string name="basic_status" msgid="2315371112182658176">"సంభాషణను తెరవండి"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> యాక్టివ్‌గా ఉన్న యాప్‌లు</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> యాక్టివ్‌గా ఉన్న యాప్</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"కొత్త సమాచారం"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"యాక్టివ్‌గా ఉన్న యాప్‌లు"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ఆపివేయండి"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ఆపివేయబడింది"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"కాపీ చేసిన టెక్స్ట్‌ను ఎడిట్ చేయండి"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"కాపీ చేసిన ఇమేజ్‌లను ఎడిట్ చేయండి"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"సమీపంలోని పరికరానికి పంపండి"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"జోడించండి"</string>
+    <string name="manage_users" msgid="1823875311934643849">"యూజర్‌లను మేనేజ్ చేయండి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index a207447..23c8c56 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(ยกเลิกการเชื่อมต่อแล้ว)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"เปลี่ยนไม่ได้ แตะเพื่อลองอีกครั้ง"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิลด์"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิลด์ไปยังคลิปบอร์ดแล้ว"</string>
     <string name="basic_status" msgid="2315371112182658176">"เปิดการสนทนา"</string>
@@ -877,8 +881,7 @@
       <item quantity="other">มี <xliff:g id="COUNT_1">%s</xliff:g> แอปที่ใช้งานอยู่</item>
       <item quantity="one">มี <xliff:g id="COUNT_0">%s</xliff:g> แอปที่ใช้งานอยู่</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"ข้อมูลใหม่"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"แอปที่ใช้งานอยู่"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"หยุด"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"หยุดแล้ว"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"คัดลอกแล้ว"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"จาก <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"ปิด UI การคัดลอก"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"แก้ไขข้อความที่คัดลอก"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"แก้ไขรูปภาพที่คัดลอก"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ส่งไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string>
+    <string name="add" msgid="81036585205287996">"เพิ่ม"</string>
+    <string name="manage_users" msgid="1823875311934643849">"จัดการผู้ใช้"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index db9c788..ce652b8 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(nadiskonekta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Hindi makalipat. I-tap para subukan ulit."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string>
     <string name="basic_status" msgid="2315371112182658176">"Buksan ang pag-uusap"</string>
@@ -877,8 +881,7 @@
       <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aktibong app</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> na aktibong app</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Bagong impormasyon"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Mga aktibong app"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ihinto"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Inihinto"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nakopya"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Mula sa <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"I-dismiss ang UI ng pagkopya"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"I-edit ang kinopyang text"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"I-edit ang kinopyang larawan"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Ipadala sa kalapit na device"</string>
+    <string name="add" msgid="81036585205287996">"Magdagdag"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Pamahalaan ang mga user"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 308af4f..24005f2 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(bağlantı kesildi)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Geçiş yapılamıyor. Tekrar denemek için dokunun."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string>
     <string name="basic_status" msgid="2315371112182658176">"Görüşmeyi aç"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> etkin uygulama</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> etkin uygulama</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Yeni bilgi"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Etkin uygulamalar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Durdur"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Durduruldu"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Kopyalandı"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulamasından"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Kopyalanan kullanıcı arayüzünü kapat"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Kopyalanan metni düzenle"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Kopyalanan resmi düzenle"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yakındaki cihaza gönder"</string>
+    <string name="add" msgid="81036585205287996">"Ekle"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Kullanıcıları yönet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 0161060..c12490d 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -818,6 +818,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(від’єднано)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Не вдалося змінити підключення. Натисніть, щоб повторити спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string>
     <string name="basic_status" msgid="2315371112182658176">"Відкрита розмова"</string>
@@ -891,8 +895,7 @@
       <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> активних додатків</item>
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> активного додатка</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нова інформація"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активні додатки"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зупинити"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Зупинено"</string>
@@ -900,14 +903,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Скопійовано"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"З додатка <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Закрити вікно копіювання"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Редагувати скопійований текст"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Редагувати скопійоване зображення"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Надіслати на пристрій поблизу"</string>
+    <string name="add" msgid="81036585205287996">"Додати"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Керувати користувачами"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ce492dd..8fd0e25f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(غیر منسلک ہے)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"سوئچ نہیں کر سکتے۔ دوبارہ کوشش کرنے کے لیے تھپتھپائیں۔"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string>
     <string name="basic_status" msgid="2315371112182658176">"گفتگو کھولیں"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> فعال ایپس</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> فعال ایپ</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"نئی معلومات"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"فعال ایپس"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"روکیں"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"رکی ہوئی ہے"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"کاپی کردہ ٹیکسٹ میں ترمیم کریں"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"کاپی کردہ تصویر میں ترمیم کریں"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"قریبی آلے کو بھیجیں"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"شامل کریں"</string>
+    <string name="manage_users" msgid="1823875311934643849">"صارفین کا نظم کریں"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 6d6c371..71778a58 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(uzildi)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Xatolik. Qayta urinish uchun bosing."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string>
     <string name="basic_status" msgid="2315371112182658176">"Suhbatni ochish"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ta faol ilova</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ta faol ilova</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Yangi axborot"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Faol ilovalar"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Toʻxtatildi"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Nusxa olindi"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Manba: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"UI nusxasini bekor qilish"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Nusxa olingan matnni tahrirlash"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Nusxa olingan rasmni tahrirlash"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Yaqin-atrofdagi qurilmaga yuborish"</string>
+    <string name="add" msgid="81036585205287996">"Kiritish"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Foydalanuvchilarni boshqarish"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5d69e03..29bc4bb 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(đã ngắt kết nối)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Không thể chuyển đổi. Hãy nhấn để thử lại."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào bảng nhớ tạm."</string>
     <string name="basic_status" msgid="2315371112182658176">"Mở cuộc trò chuyện"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ứng dụng đang hoạt động</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ứng dụng đang hoạt động</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Thông tin mới"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ứng dụng đang hoạt động"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dừng"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Đã dừng"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Đã sao chép"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"Từ <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"Đóng giao diện người dùng sao chép"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"Chỉnh sửa văn bản đã sao chép"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Chỉnh sửa hình ảnh đã sao chép"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Gửi đến thiết bị ở gần"</string>
+    <string name="add" msgid="81036585205287996">"Thêm"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Quản lý người dùng"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f032907..c553c57 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(已断开连接)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"无法切换。点按即可重试。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本号"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"已将版本号复制到剪贴板。"</string>
     <string name="basic_status" msgid="2315371112182658176">"开放式对话"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 个使用中的应用</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 个使用中的应用</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"新信息"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"使用中的应用"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"已复制"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"来自<xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"关闭复制界面"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"修改所复制的文字"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"编辑所复制的图片"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"发送到附近的设备"</string>
+    <string name="add" msgid="81036585205287996">"添加"</string>
+    <string name="manage_users" msgid="1823875311934643849">"管理用户"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index dbb4ffc..68f8704 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(已中斷連線)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"無法切換,輕按即可重試。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string>
     <string name="basic_status" msgid="2315371112182658176">"開啟對話"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 個使用中的應用程式</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 個使用中的應用程式</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資料"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"使用中的應用程式"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"已複製"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"來自「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"關閉剪貼簿使用者介面"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"編輯已複製的文字"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"編輯已複製的圖片"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"傳送至附近的裝置"</string>
+    <string name="add" msgid="81036585205287996">"新增"</string>
+    <string name="manage_users" msgid="1823875311934643849">"管理使用者"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d7185a4..c9a567e5 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(連線中斷)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"無法切換,輕觸即可重試。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string>
     <string name="basic_status" msgid="2315371112182658176">"開放式對話"</string>
@@ -877,8 +881,7 @@
       <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 個使用中的應用程式</item>
       <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 個使用中的應用程式</item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"使用中的應用程式"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
@@ -886,14 +889,9 @@
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"已複製"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"來自「<xliff:g id="APPNAME">%1$s</xliff:g>」"</string>
     <string name="clipboard_dismiss_description" msgid="7544573092766945657">"關閉剪貼簿 UI"</string>
-    <!-- no translation found for clipboard_edit_text_description (805254383912962103) -->
-    <skip />
-    <!-- no translation found for clipboard_edit_image_description (8904857948976041306) -->
-    <skip />
-    <!-- no translation found for clipboard_send_nearby_description (4629769637846717650) -->
-    <skip />
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="clipboard_edit_text_description" msgid="805254383912962103">"編輯複製的文字"</string>
+    <string name="clipboard_edit_image_description" msgid="8904857948976041306">"編輯複製的圖片"</string>
+    <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"傳送到鄰近裝置"</string>
+    <string name="add" msgid="81036585205287996">"新增"</string>
+    <string name="manage_users" msgid="1823875311934643849">"管理使用者"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5425b26..8b69c67 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -806,6 +806,10 @@
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(inqamukile)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Akukwazi ukushintsha. Thepha ukuze uzame futhi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhangqa idivayisi entsha"</string>
+    <!-- no translation found for media_output_dialog_launch_app_text (1527413319632586259) -->
+    <skip />
+    <!-- no translation found for media_output_dialog_unknown_launch_app_name (1084899329829371336) -->
+    <skip />
     <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string>
     <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string>
     <string name="basic_status" msgid="2315371112182658176">"Vula ingxoxo"</string>
@@ -877,8 +881,7 @@
       <item quantity="one">ama-app asebenzayo angu-<xliff:g id="COUNT_1">%s</xliff:g></item>
       <item quantity="other">ama-app asebenzayo angu-<xliff:g id="COUNT_1">%s</xliff:g></item>
     </plurals>
-    <!-- no translation found for fgs_dot_content_description (2865071539464777240) -->
-    <skip />
+    <string name="fgs_dot_content_description" msgid="2865071539464777240">"Ulwazi olusha"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ama-app asebenzayo"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Misa"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Imisiwe"</string>
@@ -889,8 +892,6 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Hlela umbhalo okopishiwe"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Hlela umfanekiso okopishiwe"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Thumela kudivayisi eseduze"</string>
-    <!-- no translation found for add (81036585205287996) -->
-    <skip />
-    <!-- no translation found for manage_users (1823875311934643849) -->
-    <skip />
+    <string name="add" msgid="81036585205287996">"Faka"</string>
+    <string name="manage_users" msgid="1823875311934643849">"Phatha abasebenzisi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 2992859..c5e005c 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -199,5 +199,9 @@
     </declare-styleable>
 
     <attr name="overlayButtonTextColor" format="color" />
+
+    <declare-styleable name="DreamOverlayDotImageView">
+        <attr name="dotColor" format="color" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index faf518e..dc74700 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -87,7 +87,7 @@
     <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
 
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
-    <color name="user_switcher_fullscreen_bg">@android:color/system_neutral1_900</color>
+    <color name="user_switcher_fullscreen_bg">@color/material_dynamic_neutral10</color>
     <color name="user_switcher_fullscreen_popup_item_tint">@*android:color/text_color_primary_device_default_dark</color>
 
     <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
@@ -112,7 +112,7 @@
     <!-- Chosen so fill over background matches single tone -->
     <color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
 
-    <color name="notif_pill_text">@android:color/system_neutral1_900</color>
+    <color name="notif_pill_text">@color/material_dynamic_neutral10</color>
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
@@ -131,7 +131,7 @@
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_dim_color">#80000000</color>              <!-- 50% black -->
     <color name="biometric_dialog_gray">#ff757575</color>
-    <color name="biometric_dialog_accent">@android:color/system_accent1_600</color>
+    <color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
     <!-- UDFPS colors -->
@@ -155,6 +155,7 @@
     <color name="GM2_grey_900">#202124</color>
 
     <color name="GM2_red_300">#F28B82</color>
+    <color name="GM2_red_500">#EA4335</color>
     <color name="GM2_red_700">#C5221F</color>
 
     <color name="GM2_blue_300">#8AB4F8</color>
@@ -174,11 +175,11 @@
     <color name="media_seamless_border">?android:attr/colorAccent</color>
 
     <!-- media output dialog-->
-    <color name="media_dialog_background" android:lstar="98">@android:color/system_neutral1_100</color>
-    <color name="media_dialog_active_item_main_content">@android:color/system_accent1_900</color>
-    <color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_600</color>
-    <color name="media_dialog_item_status">@android:color/system_accent1_900</color>
-    <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
+    <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
 
     <!-- controls -->
     <color name="control_primary_text">#E6FFFFFF</color>
@@ -213,7 +214,7 @@
     <!-- Wallet screen -->
     <color name="wallet_card_border">#33FFFFFF</color>
 
-    <color name="people_tile_background">@android:color/system_accent2_50</color>
+    <color name="people_tile_background">@color/material_dynamic_secondary95</color>
 
     <!-- Internet Dialog -->
     <!-- Material next state on color-->
@@ -224,4 +225,6 @@
     <color name="settingslib_track_off_color">@color/settingslib_track_off</color>
     <color name="connected_network_primary_color">#191C18</color>
     <color name="connected_network_secondary_color">#41493D</color>
+
+    <color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bb1ffa8..178f93a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -608,10 +608,6 @@
     <!-- Component name of communal source service -->
     <string name="config_communalSourceComponent" translatable="false">@null</string>
 
-    <!-- Whether idle mode should be enabled. When enabled, the lock screen will timeout to an idle
-         screen on inactivity. -->
-    <bool name="config_enableIdleMode">false</bool>
-
     <!-- This value is used when calculating whether the device is in ambient light mode. It is
         light mode when the light sensor sample value exceeds above this value. -->
     <integer name="config_ambientLightModeThreshold">5</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 47ffb18..fcf60bf 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1038,6 +1038,7 @@
     <dimen name="control_spinner_padding_horizontal">20dp</dimen>
     <dimen name="control_text_size">14sp</dimen>
     <dimen name="control_icon_size">24dp</dimen>
+    <dimen name="control_chevron_icon_size">20dp</dimen>
     <dimen name="control_spacing">8dp</dimen>
     <dimen name="control_list_divider">1dp</dimen>
     <dimen name="control_corner_radius">14dp</dimen>
@@ -1121,6 +1122,7 @@
     <dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
     <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
     <dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
+    <dimen name="media_output_dialog_app_tier_icon_size">20dp</dimen>
 
     <!-- Distance that the full shade transition takes in order for qs to fully transition to the
          shade -->
@@ -1330,12 +1332,16 @@
     <dimen name="fgs_manager_min_width_minor">100%</dimen>
 
     <!-- Dream overlay related dimensions -->
-    <dimen name="dream_overlay_status_bar_height">80dp</dimen>
+    <dimen name="dream_overlay_status_bar_height">60dp</dimen>
     <dimen name="dream_overlay_status_bar_margin">40dp</dimen>
     <dimen name="dream_overlay_status_icon_margin">8dp</dimen>
+    <dimen name="dream_overlay_status_bar_icon_size">
+        @*android:dimen/status_bar_system_icon_size</dimen>
     <!-- Height of the area at the top of the dream overlay to allow dragging down the notifications
          shade. -->
     <dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen>
+    <dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen>
+    <dimen name="dream_overlay_notification_indicator_size">6dp</dimen>
 
     <!-- Dream overlay complications related dimensions -->
     <dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3b7e9d4..df16b0d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1982,6 +1982,12 @@
     <!-- Text for privacy dialog, indicating that an app (or multiple) is using an op on behalf of another [CHAR LIMIT=NONE] -->
     <string name="ongoing_privacy_dialog_attribution_text">(through <xliff:g id="application name(s)" example="Maps, and Assistant">%s</xliff:g>)</string>
 
+    <!-- Text for privacy dialog, displaying just the subattribution label [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_attribution_label">(<xliff:g id="attribution_label" example="For Wallet">%s</xliff:g>)</string>
+
+    <!-- Text for privacy dialog, displaying both subattribution and proxy label [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_attribution_proxy_label">(<xliff:g id="attribution_label" example="For Wallet">%1$s</xliff:g>  \u2022 <xliff:g id="proxy_label" example="Maps, and Assistant">%2$s</xliff:g>)</string>
+
     <!-- Text for camera app op [CHAR LIMIT=20]-->
     <string name="privacy_type_camera">camera</string>
 
@@ -2205,6 +2211,11 @@
     <string name="media_output_dialog_connect_failed">Can\'t switch. Tap to try again.</string>
     <!-- Title for pairing item [CHAR LIMIT=60] -->
     <string name="media_output_dialog_pairing_new">Pair new device</string>
+    <!-- Title for launch app [CHAR LIMIT=60] -->
+    <string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string>
+    <!-- App name when can't get app name [CHAR LIMIT=60] -->
+    <string name="media_output_dialog_unknown_launch_app_name">Unknown app</string>
+
 
     <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
     <string name="build_number_clip_data_label">Build number</string>
@@ -2396,4 +2407,20 @@
     <string name="add_user_supervised" translatable="false">@*android:string/supervised_user_creation_label</string>
     <!-- Manage users - For system user management [CHAR LIMIT=40]  -->
     <string name="manage_users">Manage users</string>
+
+    <!-- Toast shown when a notification does not support dragging to split [CHAR LIMIT=NONE] -->
+    <string name="drag_split_not_supported">This notification does not support dragging to Splitscreen.</string>
+
+    <!-- Content description for the Wi-Fi off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_wifi_off">Wi\u2011Fi unavailable</string>
+    <!-- Content description for the priority mode icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_priority_mode">Priority mode</string>
+    <!-- Content description for the alarm set icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_alarm_set">Alarm set</string>
+    <!-- Content description for the assistant guest mode enabled icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_assistant_guest_mode_enabled">Assistant guest mode enabled</string>
+    <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
+    <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+    <string name="dream_overlay_status_bar_notification_indicator">There are notifications</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b983545..f2eaa75 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -296,7 +296,7 @@
         <item name="darkIconTheme">@style/DualToneDarkTheme</item>
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
-        <item name="wallpaperTextColorAccent">@*android:color/system_accent1_100</item>
+        <item name="wallpaperTextColorAccent">@color/material_dynamic_primary90</item>
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
         <item name="passwordStyle">@style/PasswordTheme</item>
@@ -312,7 +312,7 @@
     <style name="Theme.SystemUI.LightWallpaper">
         <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
-        <item name="wallpaperTextColorAccent">@*android:color/system_accent2_600</item>
+        <item name="wallpaperTextColorAccent">@color/material_dynamic_secondary40</item>
         <item name="android:colorError">@*android:color/error_color_material_light</item>
         <item name="shadowRadius">0</item>
 
@@ -353,9 +353,9 @@
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="android:windowIsFloating">true</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
-        <item name="offStateColor">@android:color/system_neutral1_800</item>
-        <item name="underSurfaceColor">@android:color/system_neutral1_1000</item>
-        <item name="android:colorBackground">@android:color/system_neutral1_900</item>
+        <item name="offStateColor">@color/material_dynamic_neutral20</item>
+        <item name="underSurfaceColor">@color/material_dynamic_neutral0</item>
+        <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
         <item name="android:itemTextAppearance">@style/Control.MenuItem</item>
     </style>
 
@@ -381,12 +381,19 @@
         <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
         <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
         <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+        <item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
+        <item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
     </style>
 
     <style name="AlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
         <item name="android:layout">@layout/alert_dialog_systemui</item>
     </style>
 
+    <style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog">
+        <item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item>
+        <item name="android:paddingBottom">@dimen/dialog_bottom_padding</item>
+    </style>
+
     <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
 
     <style name="Theme.SystemUI.Dialog.GlobalActions" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
@@ -399,6 +406,11 @@
         <!-- that would otherwise be intercepted by the Shade. -->
         <item name="android:windowFullscreen">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
+
+        <!-- Empty enter/exit animation, we will animate in-window. Note that the implementation -->
+        <!-- of ActionsDialogLite relies on this to be null (resource=0) to detect when to run -->
+        <!-- the in-window animation. -->
+        <item name="android:windowAnimationStyle">@null</item>
     </style>
 
     <style name="QSBorderlessButton">
@@ -573,7 +585,7 @@
     <!-- Media controls always have light background -->
     <style name="MediaPlayer" parent="@*android:style/Theme.DeviceDefault.Light">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:backgroundTint">@android:color/system_accent2_50</item>
+        <item name="android:backgroundTint">@color/material_dynamic_secondary95</item>
     </style>
 
     <style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
@@ -882,8 +894,8 @@
     </style>
 
     <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
-      <item name="android:colorBackground">@android:color/system_neutral1_900</item>
-      <item name="android:itemBackground">@android:color/system_neutral1_800</item>
+      <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+      <item name="android:itemBackground">@color/material_dynamic_neutral20</item>
       <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen.  -->
       <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
     </style>
@@ -957,6 +969,11 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
+    <style name="Widget.Dialog.Button.Large">
+        <item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
+        <item name="android:minHeight">56dp</item>
+    </style>
+
     <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
         <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
deleted file mode 100644
index 0241c59..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.shared.recents.utilities;
-
-import static android.os.Trace.TRACE_TAG_APP;
-
-/**
- * Helper class for internal trace functions.
- */
-public class AppTrace {
-
-    /**
-     * Begins a new async trace section with the given {@param key} and {@param cookie}.
-     */
-    public static void start(String key, int cookie) {
-        android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, cookie);
-    }
-
-    /**
-     * Begins a new async trace section with the given {@param key}.
-     */
-    public static void start(String key) {
-        android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, 0);
-    }
-
-    /**
-     * Ends an existing async trace section with the given {@param key}.
-     */
-    public static void end(String key) {
-        android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, 0);
-    }
-
-    /**
-     * Ends an existing async trace section with the given {@param key} and {@param cookie}.
-     */
-    public static void end(String key, int cookie) {
-        android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, cookie);
-    }
-
-    /**
-     * Begins a new trace section with the given {@param key}. Can be nested.
-     */
-    public static void beginSection(String key) {
-        android.os.Trace.beginSection(key);
-    }
-
-    /**
-     * Ends an existing trace section started in the last {@link #beginSection(String)}.
-     */
-    public static void endSection() {
-        android.os.Trace.endSection();
-    }
-
-    /**
-     * Traces a counter value.
-     */
-    public static void count(String name, int count) {
-        android.os.Trace.traceCounter(TRACE_TAG_APP, name, count);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/RectFEvaluator.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/RectFEvaluator.java
deleted file mode 100644
index 51c1b5a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/RectFEvaluator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.shared.recents.utilities;
-
-import android.animation.TypeEvaluator;
-import android.graphics.RectF;
-
-/**
- * This evaluator can be used to perform type interpolation between <code>RectF</code> values.
- */
-public class RectFEvaluator implements TypeEvaluator<RectF> {
-
-    private final RectF mRect = new RectF();
-
-    /**
-     * This function returns the result of linearly interpolating the start and
-     * end Rect values, with <code>fraction</code> representing the proportion
-     * between the start and end values. The calculation is a simple parametric
-     * calculation on each of the separate components in the Rect objects
-     * (left, top, right, and bottom).
-     *
-     * <p>The object returned will be the <code>reuseRect</code> passed into the constructor.</p>
-     *
-     * @param fraction   The fraction from the starting to the ending values
-     * @param startValue The start Rect
-     * @param endValue   The end Rect
-     * @return A linear interpolation between the start and end values, given the
-     *         <code>fraction</code> parameter.
-     */
-    @Override
-    public RectF evaluate(float fraction, RectF startValue, RectF endValue) {
-        float left = startValue.left + ((endValue.left - startValue.left) * fraction);
-        float top = startValue.top + ((endValue.top - startValue.top) * fraction);
-        float right = startValue.right + ((endValue.right - startValue.right) * fraction);
-        float bottom = startValue.bottom + ((endValue.bottom - startValue.bottom) * fraction);
-        mRect.set(left, top, right, bottom);
-        return mRect;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
index 0c7e56e..0f937bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -42,31 +42,4 @@
     public void unregisterRemoteAnimations() {
         mWrapped.unregisterRemoteAnimations();
     }
-
-    /**
-     * @see android.view.ViewDebug#dumpv2(View, ByteArrayOutputStream)
-     */
-    public boolean encodeViewHierarchy(ByteArrayOutputStream out) {
-        View view = null;
-        if (mWrapped.getWindow() != null &&
-                mWrapped.getWindow().peekDecorView() != null &&
-                mWrapped.getWindow().peekDecorView().getViewRootImpl() != null) {
-            view = mWrapped.getWindow().peekDecorView().getViewRootImpl().getView();
-        }
-        if (view == null) {
-            return false;
-        }
-
-        final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out);
-        int[] location = view.getLocationOnScreen();
-        encoder.addProperty("window:left", location[0]);
-        encoder.addProperty("window:top", location[1]);
-        view.encode(encoder);
-        encoder.endStream();
-        return true;
-    }
-
-    public int getDisplayId() {
-        return mWrapped.getDisplayId();
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 48fcbbd..461c2dc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -262,7 +262,6 @@
      * Starts a task from Recents synchronously.
      */
     public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) {
-        ActivityOptionsCompat.addTaskInfo(options, taskKey);
         return startActivityFromRecents(taskKey.id, options);
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index e2ca349..db62f88 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -104,15 +104,4 @@
         opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis);
         return opts;
     }
-
-    /**
-     * Sets Task specific information to the activity options
-     */
-    public static void addTaskInfo(ActivityOptions opts, Task.TaskKey taskKey) {
-        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            // We show non-visible docked tasks in Recents, but we always want to launch
-            // them in the fullscreen stack.
-            opts.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        }
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java
deleted file mode 100644
index 0b1141e..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java
+++ /dev/null
@@ -1,39 +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.shared.system;
-
-import android.content.ClipDescription;
-import android.content.Intent;
-
-/**
- * Wrapper around ClipDescription.
- */
-public abstract class ClipDescriptionCompat {
-
-    public static String MIMETYPE_APPLICATION_ACTIVITY =
-            ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
-
-    public static String MIMETYPE_APPLICATION_SHORTCUT =
-            ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
-
-    public static String MIMETYPE_APPLICATION_TASK =
-            ClipDescription.MIMETYPE_APPLICATION_TASK;
-
-    public static String EXTRA_PENDING_INTENT = ClipDescription.EXTRA_PENDING_INTENT;
-
-    public static String EXTRA_TASK_ID = Intent.EXTRA_TASK_ID;
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ConfigurationCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ConfigurationCompat.java
deleted file mode 100644
index d1c77a6..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ConfigurationCompat.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.shared.system;
-
-import android.content.res.Configuration;
-
-/**
- * Wraps the Configuration to access the window configuration.
- */
-public class ConfigurationCompat {
-
-    public static int getWindowConfigurationRotation(Configuration c) {
-        return c.windowConfiguration.getRotation();
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java
deleted file mode 100644
index bb319e6..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/DockedStackListenerCompat.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.system;
-
-import android.view.IDockedStackListener;
-
-/**
- * An interface to track docked stack changes.
- */
-public class DockedStackListenerCompat {
-
-    IDockedStackListener.Stub mListener = new IDockedStackListener.Stub() {
-        @Override
-        public void onDividerVisibilityChanged(boolean visible) {}
-
-        @Override
-        public void onDockedStackExistsChanged(boolean exists) {
-            DockedStackListenerCompat.this.onDockedStackExistsChanged(exists);
-        }
-
-        @Override
-        public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
-                boolean isHomeStackResizable) {
-            DockedStackListenerCompat.this.onDockedStackMinimizedChanged(minimized, animDuration,
-                    isHomeStackResizable);
-        }
-
-        @Override
-        public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {}
-
-        @Override
-        public void onDockSideChanged(final int newDockSide) {
-            DockedStackListenerCompat.this.onDockSideChanged(newDockSide);
-        }
-    };
-
-    public void onDockedStackExistsChanged(boolean exists) {
-        // To be overridden
-    }
-
-    public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
-            boolean isHomeStackResizable) {
-        // To be overridden
-    }
-
-    public void onDockSideChanged(final int newDockSide) {
-        // To be overridden
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
index a8c19ec..e6ae19e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
@@ -24,14 +24,6 @@
  * @see LatencyTracker
  */
 public class LatencyTrackerCompat {
-    /**
-     * @see LatencyTracker
-     * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead.
-     */
-    @Deprecated
-    public static void logToggleRecents(int duration) {
-        LatencyTracker.logActionDeprecated(LatencyTracker.ACTION_TOGGLE_RECENTS, duration, false);
-    }
 
     /** @see LatencyTracker */
     public static void logToggleRecents(Context context, int duration) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java
deleted file mode 100644
index d24c779..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java
+++ /dev/null
@@ -1,34 +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.shared.system;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.pm.LauncherApps;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-/**
- * Wrapper around LauncherApps.
- */
-public abstract class LauncherAppsCompat {
-
-    public static PendingIntent getMainActivityLaunchIntent(LauncherApps launcherApps,
-            ComponentName component, Bundle startActivityOptions, UserHandle user) {
-        return launcherApps.getMainActivityLaunchIntent(component, startActivityOptions, user);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherEventUtil.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherEventUtil.java
deleted file mode 100644
index a51d668..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherEventUtil.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-public class LauncherEventUtil {
-
-    // Constants for the Action
-    public static final int VISIBLE = 0;
-    public static final int DISMISS = 1;
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
deleted file mode 100644
index 952c8ae..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/MetricsLoggerCompat.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-public class MetricsLoggerCompat {
-
-    private final MetricsLogger mMetricsLogger;
-    public static final int OVERVIEW_ACTIVITY = MetricsEvent.OVERVIEW_ACTIVITY;
-
-    public MetricsLoggerCompat() {
-        mMetricsLogger = new MetricsLogger();
-    }
-
-    public void action(int category) {
-        mMetricsLogger.action(category);
-    }
-
-    public void action(int category, int value) {
-        mMetricsLogger.action(category, value);
-    }
-
-    public void visible(int category) {
-        mMetricsLogger.visible(category);
-    }
-
-    public void hidden(int category) {
-        mMetricsLogger.hidden(category);
-    }
-
-    public void visibility(int category, boolean visible) {
-        mMetricsLogger.visibility(category, visible);
-    }
-}
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 08b4d3f..2b1c47f 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
@@ -103,8 +103,8 @@
     // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
     // stack.
     public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
-    // The global actions dialog is showing
-    public static final int SYSUI_STATE_GLOBAL_ACTIONS_SHOWING = 1 << 15;
+    // A SysUI dialog is showing.
+    public static final int SYSUI_STATE_DIALOG_SHOWING = 1 << 15;
     // The one-handed mode is active
     public static final int SYSUI_STATE_ONE_HANDED_ACTIVE = 1 << 16;
     // Allow system gesture no matter the system bar(s) is visible or not
@@ -140,7 +140,7 @@
             SYSUI_STATE_TRACING_ENABLED,
             SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
             SYSUI_STATE_BUBBLES_EXPANDED,
-            SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
+            SYSUI_STATE_DIALOG_SHOWING,
             SYSUI_STATE_ONE_HANDED_ACTIVE,
             SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
             SYSUI_STATE_IME_SHOWING,
@@ -166,7 +166,7 @@
         str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0
                 ? "keygrd_occluded" : "");
         str.add((flags & SYSUI_STATE_BOUNCER_SHOWING) != 0 ? "bouncer_visible" : "");
-        str.add((flags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0 ? "global_actions" : "");
+        str.add((flags & SYSUI_STATE_DIALOG_SHOWING) != 0 ? "dialog_showing" : "");
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
         str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
@@ -256,7 +256,7 @@
     public static boolean isBackGestureDisabled(int sysuiStateFlags) {
         // Always allow when the bouncer/global actions is showing (even on top of the keyguard)
         if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
-                || (sysuiStateFlags & SYSUI_STATE_GLOBAL_ACTIONS_SHOWING) != 0) {
+                || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0) {
             return false;
         }
         if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
deleted file mode 100644
index 7c8c23e..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.shared.system;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IRotationWatcher;
-import android.view.WindowManagerGlobal;
-
-public abstract class RotationWatcher {
-
-    private static final String TAG = "RotationWatcher";
-
-    private final Context mContext;
-
-    private final IRotationWatcher mWatcher = new IRotationWatcher.Stub() {
-
-        @Override
-        public void onRotationChanged(int rotation) {
-            RotationWatcher.this.onRotationChanged(rotation);
-
-        }
-    };
-
-    private boolean mIsWatching = false;
-
-    public RotationWatcher(Context context) {
-        mContext = context;
-    }
-
-    protected abstract void onRotationChanged(int rotation);
-
-    public void enable() {
-        if (!mIsWatching) {
-            try {
-                WindowManagerGlobal.getWindowManagerService().watchRotation(mWatcher,
-                        mContext.getDisplayId());
-                mIsWatching = true;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to set rotation watcher", e);
-            }
-        }
-    }
-
-    public void disable() {
-        if (mIsWatching) {
-            try {
-                WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mWatcher);
-                mIsWatching = false;
-            }  catch (RemoteException e) {
-                Log.w(TAG, "Failed to remove rotation watcher", e);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
deleted file mode 100644
index 35952f5..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.app.ActivityManager;
-import android.graphics.Bitmap;
-
-public class TaskDescriptionCompat {
-
-    private ActivityManager.TaskDescription mTaskDescription;
-
-    public TaskDescriptionCompat(ActivityManager.TaskDescription td) {
-        mTaskDescription = td;
-    }
-
-    public int getPrimaryColor() {
-        return mTaskDescription != null
-                ? mTaskDescription.getPrimaryColor()
-                : 0;
-    }
-
-    public int getBackgroundColor() {
-        return mTaskDescription != null
-                ? mTaskDescription.getBackgroundColor()
-                : 0;
-    }
-
-    public static Bitmap getIcon(ActivityManager.TaskDescription desc, int userId) {
-        if (desc.getInMemoryIcon() != null) {
-            return desc.getInMemoryIcon();
-        }
-        return ActivityManager.TaskDescription.loadTaskDescriptionIcon(
-                desc.getIconFilename(), userId);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java
deleted file mode 100644
index ffd8a08..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.view.ThreadedRenderer;
-
-/**
- * @see ThreadedRenderer
- */
-public class ThreadedRendererCompat {
-
-    public static int EGL_CONTEXT_PRIORITY_REALTIME_NV = 0x3357;
-    public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
-    public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
-    public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
-
-    public static void setContextPriority(int priority) {
-        ThreadedRenderer.setContextPriority(priority);
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java
deleted file mode 100644
index 4a0f89b..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TonalCompat.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.shared.system;
-
-import android.app.WallpaperColors;
-import android.content.Context;
-
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.colorextraction.types.Tonal;
-
-public class TonalCompat {
-
-    private final Tonal mTonal;
-
-    public TonalCompat(Context context) {
-        mTonal = new Tonal(context);
-    }
-
-    public ExtractionInfo extractDarkColors(WallpaperColors colors) {
-        GradientColors darkColors = new GradientColors();
-        mTonal.extractInto(colors, new GradientColors(), darkColors, new GradientColors());
-
-        ExtractionInfo result = new ExtractionInfo();
-        result.mainColor = darkColors.getMainColor();
-        result.secondaryColor = darkColors.getSecondaryColor();
-        result.supportsDarkText = darkColors.supportsDarkText();
-        if (colors != null) {
-            result.supportsDarkTheme =
-                    (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
-        }
-        return result;
-    }
-
-    public static class ExtractionInfo {
-        public int mainColor;
-        public int secondaryColor;
-        public boolean supportsDarkText;
-        public boolean supportsDarkTheme;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
deleted file mode 100644
index 89c60f1..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ /dev/null
@@ -1,67 +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.shared.system;
-
-import android.graphics.HardwareRenderer;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewRootImpl;
-
-import java.util.function.LongConsumer;
-
-/**
- * Helper class to expose some ViewRoomImpl methods
- */
-public class ViewRootImplCompat {
-
-    private final ViewRootImpl mViewRoot;
-
-    public ViewRootImplCompat(View view) {
-        mViewRoot = view == null ? null : view.getViewRootImpl();
-    }
-
-    public SurfaceControl getRenderSurfaceControl() {
-        return mViewRoot == null ? null : mViewRoot.getSurfaceControl();
-    }
-
-    public boolean isValid() {
-        return mViewRoot != null;
-    }
-
-    public View getView() {
-        return mViewRoot == null ? null : mViewRoot.getView();
-    }
-
-    public void registerRtFrameCallback(LongConsumer callback) {
-        if (mViewRoot != null) {
-            mViewRoot.registerRtFrameCallback(
-                    new HardwareRenderer.FrameDrawingCallback() {
-                        @Override
-                        public void onFrameDraw(long l) {
-                            callback.accept(l);
-                        }
-                    });
-        }
-    }
-
-    public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
-        if (mViewRoot != null) {
-            mViewRoot.mergeWithNextTransaction(t, frame);
-        } else {
-            t.apply();
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java
deleted file mode 100644
index 73dc60d..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java
+++ /dev/null
@@ -1,56 +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.shared.system;
-
-import android.graphics.Rect;
-import android.service.wallpaper.IWallpaperEngine;
-import android.util.Log;
-
-/**
- * @see IWallpaperEngine
- */
-public class WallpaperEngineCompat {
-
-    private static final String TAG = "WallpaperEngineCompat";
-
-    /**
-     * Returns true if {@link IWallpaperEngine#scalePreview(Rect)} is available.
-     */
-    public static boolean supportsScalePreview() {
-        try {
-            return IWallpaperEngine.class.getMethod("scalePreview", Rect.class) != null;
-        } catch (NoSuchMethodException | SecurityException e) {
-            return false;
-        }
-    }
-
-    private final IWallpaperEngine mWrappedEngine;
-
-    public WallpaperEngineCompat(IWallpaperEngine wrappedEngine) {
-        mWrappedEngine = wrappedEngine;
-    }
-
-    /**
-     * @see IWallpaperEngine#scalePreview(Rect)
-     */
-    public void scalePreview(Rect scaleToRect) {
-        try {
-            mWrappedEngine.scalePreview(scaleToRect);
-        } catch (Exception e) {
-            Log.i(TAG, "Couldn't call scalePreview method on WallpaperEngine", e);
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
deleted file mode 100644
index 1f194eca..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
+++ /dev/null
@@ -1,51 +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.shared.system;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.IBinder;
-
-/**
- * @see WallpaperManager
- */
-public class WallpaperManagerCompat {
-    private final WallpaperManager mWallpaperManager;
-
-    public WallpaperManagerCompat(Context context) {
-        mWallpaperManager = context.getSystemService(WallpaperManager.class);
-    }
-
-    /**
-     * @see WallpaperManager#setWallpaperZoomOut(IBinder, float)
-     */
-    public void setWallpaperZoomOut(IBinder windowToken, float zoom) {
-        mWallpaperManager.setWallpaperZoomOut(windowToken, zoom);
-    }
-
-    /**
-     * @return the max scale for the wallpaper when it's fully zoomed out
-     */
-    public static float getWallpaperZoomOutMaxScale(Context context) {
-        return context.getResources()
-                .getFloat(Resources.getSystem().getIdentifier(
-                        /* name= */ "config_wallpaperMaxScale",
-                        /* defType= */ "dimen",
-                        /* defPackage= */ "android"));
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 8bcb7c9..ccbb0c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -43,6 +43,7 @@
     private static final String TAG = "KeyguardEsimArea";
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
+    private int mSubscriptionId;
     private EuiccManager mEuiccManager;
 
     private BroadcastReceiver mReceiver =
@@ -87,6 +88,10 @@
         setOnClickListener(this);
     }
 
+    public void setSubscriptionId(int subscriptionId) {
+        mSubscriptionId = subscriptionId;
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -113,6 +118,12 @@
 
     @Override
     public void onClick(View v) {
+        SubscriptionInfo sub = SubscriptionManager.from(mContext)
+                .getActiveSubscriptionInfo(mSubscriptionId);
+        if (sub == null) {
+            Log.e(TAG, "No active subscription with subscriptionId: " + mSubscriptionId);
+            return;
+        }
         Intent intent = new Intent(ACTION_DISABLE_ESIM);
         intent.setPackage(mContext.getPackageName());
         PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
@@ -120,7 +131,7 @@
             0 /* requestCode */,
             intent,
             PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED, UserHandle.SYSTEM);
-        mEuiccManager
-                .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
+        mEuiccManager.switchToSubscription(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, sub.getPortIndex(), callbackIntent);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0f9ce7..736e34e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -37,8 +37,9 @@
         super(context, attrs);
     }
 
-    public void setEsimLocked(boolean locked) {
+    public void setEsimLocked(boolean locked, int subscriptionId) {
         KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
+        esimButton.setSubscriptionId(subscriptionId);
         esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index e04bfdc..0394e76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -103,7 +103,7 @@
             showDefaultMessage();
         }
 
-        mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+        mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 0730922..ea94b19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -169,6 +169,7 @@
             boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
 
             KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+            esimButton.setSubscriptionId(mSubId);
             esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
             mPasswordEntry.requestFocus();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b32c2b6..84b6ace 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -38,6 +38,7 @@
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.appops.AppOpsController;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -107,6 +108,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -372,6 +374,8 @@
     @Inject Lazy<AmbientState> mAmbientStateLazy;
     @Inject Lazy<GroupMembershipManager> mGroupMembershipManagerLazy;
     @Inject Lazy<GroupExpansionManager> mGroupExpansionManagerLazy;
+    @Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy;
+    @Inject Lazy<DialogLaunchAnimator> mDialogLaunchAnimatorLazy;
 
     @Inject
     public Dependency() {
@@ -592,6 +596,8 @@
         mProviders.put(AmbientState.class, mAmbientStateLazy::get);
         mProviders.put(GroupMembershipManager.class, mGroupMembershipManagerLazy::get);
         mProviders.put(GroupExpansionManager.class, mGroupExpansionManagerLazy::get);
+        mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get);
+        mProviders.put(DialogLaunchAnimator.class, mDialogLaunchAnimatorLazy::get);
 
         Dependency.setInstance(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 23ca923..3a6165c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -38,13 +38,13 @@
 import android.util.Log;
 import android.util.TimingsTraceLog;
 import android.view.SurfaceControl;
+import android.view.ThreadedRenderer;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.systemui.dagger.ContextComponentHelper;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.shared.system.ThreadedRendererCompat;
 import com.android.systemui.util.NotificationChannels;
 
 import java.lang.reflect.Constructor;
@@ -118,11 +118,11 @@
             // The priority is defaulted at medium.
             int sfPriority = SurfaceControl.getGPUContextPriority();
             Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
-            if (sfPriority == ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
+            if (sfPriority == ThreadedRenderer.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
                 Log.i(TAG, "Setting SysUI's GPU Context priority to: "
-                        + ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-                ThreadedRendererCompat.setContextPriority(
-                        ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                        + ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                ThreadedRenderer.setContextPriority(
+                        ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
             }
 
             // Enable binder tracing on system server for calls originating from SysUI
@@ -192,6 +192,7 @@
         Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
                 Comparator.comparing(Class::getName));
         sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
+        sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
         startServicesIfNeeded(
                 sortedStartables, "StartServices", vendorComponent);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 11fffd0..f5084f5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -21,6 +21,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -28,6 +29,7 @@
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
+import com.android.wm.shell.dagger.WMShellConcurrencyModule;
 import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
 import com.android.wm.shell.transition.ShellTransitions;
@@ -96,8 +98,9 @@
                 && android.os.Process.myUserHandle().isSystem()
                 && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
         mRootComponent = buildGlobalRootComponent(context);
+
         // Stand up WMComponent
-        mWMComponent = mRootComponent.getWMComponentBuilder().build();
+        setupWmComponent(context);
         if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
@@ -124,8 +127,8 @@
                     .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
-                    .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
-                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()))
+                    .setCompatUI(mWMComponent.getCompatUI())
+                    .setDragAndDrop(mWMComponent.getDragAndDrop())
                     .setBackAnimation(mWMComponent.getBackAnimation());
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
@@ -161,6 +164,36 @@
     }
 
     /**
+     * Sets up {@link #mWMComponent}. On devices where the Shell runs on its own main thread,
+     * this will pre-create the thread to ensure that the components are constructed on the
+     * same thread, to reduce the likelihood of side effects from running the constructors on
+     * a different thread than the rest of the class logic.
+     */
+    private void setupWmComponent(Context context) {
+        WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder();
+        if (!mInitializeComponents || !WMShellConcurrencyModule.enableShellMainThread(context)) {
+            // If running under tests or shell thread is not enabled, we don't need anything special
+            mWMComponent = wmBuilder.build();
+            return;
+        }
+
+        // If the shell main thread is enabled, initialize the component on that thread
+        HandlerThread shellThread = WMShellConcurrencyModule.createShellMainThread();
+        shellThread.start();
+
+        // Use an async handler since we don't care about synchronization
+        Handler shellHandler = Handler.createAsync(shellThread.getLooper());
+        boolean built = shellHandler.runWithScissors(() -> {
+            wmBuilder.setShellMainThread(shellThread);
+            mWMComponent = wmBuilder.build();
+        }, 5000);
+        if (!built) {
+            Log.w(TAG, "Failed to initialize WMComponent");
+            throw new RuntimeException();
+        }
+    }
+
+    /**
      * Prepares the SysUIComponent builder before it is built.
      * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
      * @param wm the built WMComponent from the root component's getWMComponent() method
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0d20403..de03993 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -43,6 +43,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Range;
+import android.util.Size;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.Gravity;
@@ -62,6 +63,8 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 
+import androidx.core.math.MathUtils;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.R;
@@ -166,6 +169,7 @@
     private final Rect mMagnificationFrameBoundary = new Rect();
     // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
     private int mSystemGestureTop = -1;
+    private int mMinWindowSize;
 
     private final WindowMagnificationAnimationController mAnimationController;
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@@ -208,8 +212,10 @@
         mBounceEffectDuration = mResources.getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         updateDimensions();
-        setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
-                mWindowBounds.height() / 2);
+
+        final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
+        setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(),
+                mWindowBounds.width() / 2, mWindowBounds.height() / 2);
         computeBounceAnimationScale();
 
         mMirrorWindowControl = mirrorWindowControl;
@@ -281,6 +287,8 @@
                 R.dimen.magnification_drag_view_size);
         mOuterBorderSize = mResources.getDimensionPixelSize(
                 R.dimen.magnification_outer_border_margin);
+        mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
     }
 
     private void computeBounceAnimationScale() {
@@ -414,9 +422,12 @@
             return false;
         }
         mWindowBounds.set(currentWindowBounds);
+        final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
         final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
         final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
-        setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+
+        setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX,
+                (int) newCenterY);
         calculateMagnificationFrameBoundary();
         return true;
     }
@@ -454,11 +465,6 @@
 
         mWindowBounds.set(currentWindowBounds);
 
-        calculateMagnificationFrameBoundary();
-
-        if (!isWindowVisible()) {
-            return;
-        }
         // Keep MirrorWindow position on the screen unchanged when device rotates 90°
         // clockwise or anti-clockwise.
 
@@ -469,14 +475,13 @@
         } else if (rotationDegree == 270) {
             matrix.postTranslate(0, mWindowBounds.height());
         }
-        // The rect of MirrorView is going to be transformed.
-        LayoutParams params =
-                (LayoutParams) mMirrorView.getLayoutParams();
-        mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height);
-        final RectF transformedRect = new RectF(mTmpRect);
+
+        final RectF transformedRect = new RectF(mMagnificationFrame);
+        // The window frame is going to be transformed by the rotation matrix.
+        transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin);
         matrix.mapRect(transformedRect);
-        moveWindowMagnifier(transformedRect.left - mTmpRect.left,
-                transformedRect.top - mTmpRect.top);
+        setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(),
+                (int) transformedRect.centerX(), (int) transformedRect.centerY());
     }
 
     /** Returns the rotation degree change of two {@link Surface.Rotation} */
@@ -573,16 +578,52 @@
         }
     }
 
-    private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
+    /**
+     * Sets the window size with given width and height in pixels without changing the
+     * window center. The width or the height will be clamped in the range
+     * [{@link #mMinWindowSize}, screen width or height].
+     *
+     * @param width the window width in pixels
+     * @param height the window height in pixels.
+     */
+    public void setWindowSize(int width, int height) {
+        setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN);
+    }
+
+    void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) {
+        width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width());
+        height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height());
+
+        if (Float.isNaN(centerX)) {
+            centerX = mMagnificationFrame.centerX();
+        }
+        if (Float.isNaN(centerX)) {
+            centerY = mMagnificationFrame.centerY();
+        }
+
+        final int frameWidth = width - 2 * mMirrorSurfaceMargin;
+        final int frameHeight = height - 2 * mMirrorSurfaceMargin;
+        setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY);
+        calculateMagnificationFrameBoundary();
+        // Correct the frame position to ensure it is inside the boundary.
+        updateMagnificationFramePosition(0, 0);
+        modifyWindowMagnification(true);
+    }
+
+    private void setMagnificationFrame(int width, int height, int centerX, int centerY) {
         // Sets the initial frame area for the mirror and place it to the given center on the
         // display.
+        final int initX = centerX - width / 2;
+        final int initY = centerY - height / 2;
+        mMagnificationFrame.set(initX, initY, initX + width, initY + height);
+    }
+
+    private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) {
         int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2;
         initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size),
                 initSize);
         initSize += 2 * mMirrorSurfaceMargin;
-        final int initX = centerX - initSize / 2;
-        final int initY = centerY - initSize / 2;
-        mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
+        return new Size(initSize, initSize);
     }
 
     /**
@@ -596,8 +637,7 @@
         }
         mTransaction.show(mMirrorSurface)
                 .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
-
-        modifyWindowMagnification(mTransaction);
+        modifyWindowMagnification(false);
     }
 
     private void addDragTouchListeners() {
@@ -615,18 +655,25 @@
     }
 
     /**
-     * Modifies the placement of the mirrored content when the position of mMirrorView is updated.
+     * Modifies the placement of the mirrored content when the position or size of mMirrorView is
+     * updated.
+     *
+     * @param computeWindowSize set to {@code true} to compute window size with
+     * {@link #mMagnificationFrame}.
      */
-    private void modifyWindowMagnification(SurfaceControl.Transaction t) {
+    private void modifyWindowMagnification(boolean computeWindowSize) {
         mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
-        updateMirrorViewLayout();
+        updateMirrorViewLayout(computeWindowSize);
     }
 
     /**
-     * Updates the layout params of MirrorView and translates MirrorView position when the view is
-     * moved close to the screen edges.
+     * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame}
+     * and translates MirrorView position when the view is moved close to the screen edges;
+     *
+     * @param computeWindowSize set to {@code true} to compute window size with
+     * {@link #mMagnificationFrame}.
      */
-    private void updateMirrorViewLayout() {
+    private void updateMirrorViewLayout(boolean computeWindowSize) {
         if (!isWindowVisible()) {
             return;
         }
@@ -637,6 +684,10 @@
                 (LayoutParams) mMirrorView.getLayoutParams();
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
+        if (computeWindowSize) {
+            params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
+            params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
+        }
 
         // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
         // able to move close to the screen edges.
@@ -899,7 +950,7 @@
             createMirrorWindow();
             showControls();
         } else {
-            modifyWindowMagnification(mTransaction);
+            modifyWindowMagnification(false);
         }
     }
 
@@ -930,7 +981,7 @@
             return;
         }
         if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) {
-            modifyWindowMagnification(mTransaction);
+            modifyWindowMagnification(false);
         }
     }
 
@@ -1014,9 +1065,15 @@
         pw.println("      mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
         pw.println("      mScale:" + mScale);
         pw.println("      mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+        pw.println("      mMagnificationFrameBoundary:"
+                + (isWindowVisible() ? mMagnificationFrameBoundary : "empty"));
+        pw.println("      mMagnificationFrame:"
+                + (isWindowVisible() ? mMagnificationFrame : "empty"));
         pw.println("      mSourceBounds:"
                  + (isWindowVisible() ? mSourceBounds : "empty"));
         pw.println("      mSystemGestureTop:" + mSystemGestureTop);
+        pw.println("      mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX);
+        pw.println("      mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY);
     }
 
     private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 54664f2..b60e266 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,15 +18,13 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
 
-import static java.util.Objects.requireNonNull;
-
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.provider.DeviceConfig;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.util.DeviceConfigProxy;
 
 import javax.inject.Inject;
 
@@ -37,19 +35,24 @@
 public class ClipboardListener extends CoreStartable
         implements ClipboardManager.OnPrimaryClipChangedListener {
 
+    private final DeviceConfigProxy mDeviceConfig;
+    private final ClipboardOverlayControllerFactory mOverlayFactory;
+    private final ClipboardManager mClipboardManager;
     private ClipboardOverlayController mClipboardOverlayController;
-    private ClipboardManager mClipboardManager;
 
     @Inject
-    public ClipboardListener(Context context) {
+    public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
+            ClipboardOverlayControllerFactory overlayFactory, ClipboardManager clipboardManager) {
         super(context);
+        mDeviceConfig = deviceConfigProxy;
+        mOverlayFactory = overlayFactory;
+        mClipboardManager = clipboardManager;
     }
 
     @Override
     public void start() {
-        if (DeviceConfig.getBoolean(
+        if (mDeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
-            mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
             mClipboardManager.addPrimaryClipChangedListener(this);
         }
     }
@@ -60,8 +63,7 @@
             return;
         }
         if (mClipboardOverlayController == null) {
-            mClipboardOverlayController =
-                    new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
+            mClipboardOverlayController = mOverlayFactory.create(mContext);
         }
         mClipboardOverlayController.setClipData(
                 mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource());
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
new file mode 100644
index 0000000..e1c11c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.clipboardoverlay;
+
+import android.content.Context;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.TimeoutHandler;
+
+import javax.inject.Inject;
+
+/**
+ * A factory that churns out ClipboardOverlayControllers on demand.
+ */
+@SysUISingleton
+public class ClipboardOverlayControllerFactory {
+
+    @Inject
+    public ClipboardOverlayControllerFactory() {}
+
+    /**
+     * One new ClipboardOverlayController, coming right up!
+     */
+    public ClipboardOverlayController create(Context context) {
+        return new ClipboardOverlayController(context, new TimeoutHandler(context));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
deleted file mode 100644
index 2d59e13..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.conditions;
-
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.wifi.WifiInfo;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-import javax.inject.Inject;
-
-/**
- * Monitors Wi-Fi connections and triggers callback, if any, when the device is connected to and
- * disconnected from a trusted network.
- */
-public class CommunalTrustedNetworkCondition extends Condition {
-    private final String mTag = getClass().getSimpleName();
-    private final ConnectivityManager mConnectivityManager;
-    private final ContentObserver mTrustedNetworksObserver;
-    private final SecureSettings mSecureSettings;
-
-    // The SSID of the connected Wi-Fi network. Null if not connected to Wi-Fi.
-    private String mWifiSSID;
-
-    // Set of SSIDs of trusted networks.
-    private final HashSet<String> mTrustedNetworks = new HashSet<>();
-
-    /**
-     * The deliminator used to separate trusted network keys saved as a string in secure settings.
-     */
-    public static final String SETTINGS_STRING_DELIMINATOR = ",/";
-
-    @Inject
-    public CommunalTrustedNetworkCondition(@Main Handler handler,
-            ConnectivityManager connectivityManager, SecureSettings secureSettings) {
-        mConnectivityManager = connectivityManager;
-        mSecureSettings = secureSettings;
-
-        mTrustedNetworksObserver = new ContentObserver(handler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                fetchTrustedNetworks();
-            }
-        };
-    }
-
-    /**
-     * Starts monitoring for trusted network connection. Ignores if already started.
-     */
-    @Override
-    protected void start() {
-        if (shouldLog()) Log.d(mTag, "start listening for wifi connections");
-
-        fetchTrustedNetworks();
-
-        final NetworkRequest wifiNetworkRequest = new NetworkRequest.Builder().addTransportType(
-                NetworkCapabilities.TRANSPORT_WIFI).build();
-        mConnectivityManager.registerNetworkCallback(wifiNetworkRequest, mNetworkCallback);
-        mSecureSettings.registerContentObserverForUser(
-                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, false, mTrustedNetworksObserver,
-                UserHandle.USER_SYSTEM);
-    }
-
-    /**
-     * Stops monitoring for trusted network connection.
-     */
-    @Override
-    protected void stop() {
-        if (shouldLog()) Log.d(mTag, "stop listening for wifi connections");
-
-        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
-        mSecureSettings.unregisterContentObserver(mTrustedNetworksObserver);
-    }
-
-    private void updateWifiInfo(WifiInfo wifiInfo) {
-        if (wifiInfo == null) {
-            mWifiSSID = null;
-        } else {
-            // Remove the wrapping quotes around the SSID.
-            mWifiSSID = wifiInfo.getSSID().replace("\"", "");
-        }
-
-        checkIfConnectedToTrustedNetwork();
-    }
-
-    private void fetchTrustedNetworks() {
-        final String trustedNetworksString = mSecureSettings.getStringForUser(
-                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, UserHandle.USER_SYSTEM);
-        mTrustedNetworks.clear();
-
-        if (shouldLog()) Log.d(mTag, "fetched trusted networks: " + trustedNetworksString);
-
-        if (TextUtils.isEmpty(trustedNetworksString)) {
-            return;
-        }
-
-        mTrustedNetworks.addAll(
-                Arrays.asList(trustedNetworksString.split(SETTINGS_STRING_DELIMINATOR)));
-
-        checkIfConnectedToTrustedNetwork();
-    }
-
-    private void checkIfConnectedToTrustedNetwork() {
-        final boolean connectedToTrustedNetwork = mWifiSSID != null && mTrustedNetworks.contains(
-                mWifiSSID);
-
-        if (shouldLog()) {
-            Log.d(mTag, (connectedToTrustedNetwork ? "connected to" : "disconnected from")
-                    + " a trusted network");
-        }
-
-        updateCondition(connectedToTrustedNetwork);
-    }
-
-    private final ConnectivityManager.NetworkCallback mNetworkCallback =
-            new ConnectivityManager.NetworkCallback(
-                    ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
-                private boolean mIsConnected = false;
-                private WifiInfo mWifiInfo;
-
-                @Override
-                public void onAvailable(@NonNull Network network) {
-                    super.onAvailable(network);
-
-                    if (shouldLog()) Log.d(mTag, "connected to wifi");
-
-                    mIsConnected = true;
-                    if (mWifiInfo != null) {
-                        updateWifiInfo(mWifiInfo);
-                    }
-                }
-
-                @Override
-                public void onLost(@NonNull Network network) {
-                    super.onLost(network);
-
-                    if (shouldLog()) Log.d(mTag, "disconnected from wifi");
-
-                    mIsConnected = false;
-                    mWifiInfo = null;
-                    updateWifiInfo(null);
-                }
-
-                @Override
-                public void onCapabilitiesChanged(@NonNull Network network,
-                        @NonNull NetworkCapabilities networkCapabilities) {
-                    super.onCapabilitiesChanged(network, networkCapabilities);
-
-                    mWifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
-
-                    if (mIsConnected) {
-                        updateWifiInfo(mWifiInfo);
-                    }
-                }
-            };
-
-    private boolean shouldLog() {
-        return Log.isLoggable(mTag, Log.DEBUG);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index e1f1ac4..814b251 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.TextUtils;
-import android.view.View;
-import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
 
@@ -30,9 +28,6 @@
 import com.android.systemui.communal.PackageObserver;
 import com.android.systemui.communal.conditions.CommunalSettingCondition;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.idle.AmbientLightModeMonitor;
-import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.util.condition.Condition;
 import com.android.systemui.util.condition.Monitor;
 import com.android.systemui.util.condition.dagger.MonitorComponent;
@@ -46,7 +41,6 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.ElementsIntoSet;
@@ -58,22 +52,12 @@
  */
 @Module(subcomponents = {
         CommunalViewComponent.class,
-        IdleViewComponent.class,
 })
 public interface CommunalModule {
-    String IDLE_VIEW = "idle_view";
     String COMMUNAL_CONDITIONS = "communal_conditions";
 
     /** */
     @Provides
-    @Named(IDLE_VIEW)
-    static View provideIdleView(Context context) {
-        FrameLayout view = new FrameLayout(context);
-        return view;
-    }
-
-    /** */
-    @Provides
     static Optional<CommunalSource.Observer> provideCommunalSourcePackageObserver(
             Context context, @Main Resources resources) {
         final String componentName = resources.getString(R.string.config_communalSourceComponent);
@@ -87,15 +71,6 @@
     }
 
     /**
-     * Provides LightSensorEventsDebounceAlgorithm as an instance to DebounceAlgorithm interface.
-     * @param algorithm the instance of algorithm that is bound to the interface.
-     * @return the interface that is bound to.
-     */
-    @Binds
-    AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
-            LightSensorEventsDebounceAlgorithm algorithm);
-
-    /**
      * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 47e749c..4819bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -118,6 +118,7 @@
     private var nextStatusText: CharSequence = ""
     val title: TextView = layout.requireViewById(R.id.title)
     val subtitle: TextView = layout.requireViewById(R.id.subtitle)
+    val chevronIcon: ImageView = layout.requireViewById(R.id.chevron_icon)
     val context: Context = layout.getContext()
     val clipLayer: ClipDrawable
     lateinit var cws: ControlWithState
@@ -163,6 +164,7 @@
             cws.control?.let {
                 title.setText(it.title)
                 subtitle.setText(it.subtitle)
+                chevronIcon.visibility = if (usePanel()) View.VISIBLE else View.INVISIBLE
             }
         }
 
@@ -469,6 +471,7 @@
         updateContentDescription()
 
         status.setTextColor(color)
+        chevronIcon.imageTintList = color
 
         control?.getCustomIcon()?.let {
             icon.setImageIcon(it)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index fa23842..f6aeb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -49,6 +49,7 @@
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
+import com.android.systemui.clipboardoverlay.ClipboardOverlayControllerFactory;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -291,4 +292,11 @@
     public ModeSwitchesController providesModeSwitchesController(Context context) {
         return new ModeSwitchesController(context);
     }
+
+    /***/
+    @Provides
+    @SysUISingleton
+    public ClipboardOverlayControllerFactory provideClipboardOverlayControllerFactory() {
+        return new ClipboardOverlayControllerFactory();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b51eb07..bd472a4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -31,6 +31,7 @@
 import android.app.role.RoleManager;
 import android.app.smartspace.SmartspaceManager;
 import android.app.trust.TrustManager;
+import android.content.ClipboardManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.om.OverlayManager;
@@ -451,6 +452,12 @@
 
     @Provides
     @Singleton
+    static ClipboardManager provideClipboardManager(Context context) {
+        return context.getSystemService(ClipboardManager.class);
+    }
+
+    @Provides
+    @Singleton
     static SmartspaceManager provideSmartspaceManager(Context context) {
         return context.getSystemService(SmartspaceManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index b96c5ae..13067bf 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -42,6 +42,7 @@
 import com.android.systemui.flags.FlagsModule;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.log.dagger.LogModule;
+import com.android.systemui.lowlightclock.LowLightClockController;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -86,6 +87,7 @@
 import com.android.systemui.wallet.dagger.WalletModule;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.dagger.DynamicOverride;
 
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -214,4 +216,19 @@
                 groupManager, entryManager, notifCollection, notifPipeline, sysUiState,
                 notifPipelineFlags, dumpManager, sysuiMainExecutor));
     }
+
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract LowLightClockController optionalLowLightClockController();
+
+    @SysUISingleton
+    @Provides
+    static Optional<LowLightClockController> provideLowLightClockController(
+            @DynamicOverride Optional<LowLightClockController> optionalController) {
+        if (optionalController.isPresent() && optionalController.get().isLowLightClockEnabled()) {
+            return optionalController;
+        } else {
+            return Optional.empty();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b926692..b02074a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -17,6 +17,9 @@
 package com.android.systemui.dagger;
 
 import android.content.Context;
+import android.os.HandlerThread;
+
+import androidx.annotation.Nullable;
 
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.tv.TvWMComponent;
@@ -26,6 +29,7 @@
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
 import com.android.wm.shell.dagger.WMShellModule;
@@ -44,6 +48,7 @@
 
 import java.util.Optional;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -64,6 +69,10 @@
      */
     @Subcomponent.Builder
     interface Builder {
+
+        @BindsInstance
+        Builder setShellMainThread(@Nullable @ShellMainThread HandlerThread t);
+
         WMComponent build();
     }
 
@@ -120,10 +129,10 @@
     Optional<RecentTasks> getRecentTasks();
 
     @WMSingleton
-    CompatUI getCompatUI();
+    Optional<CompatUI> getCompatUI();
 
     @WMSingleton
-    DragAndDrop getDragAndDrop();
+    Optional<DragAndDrop> getDragAndDrop();
 
     @WMSingleton
     Optional<BackAnimation> getBackAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index befb648..789ad62 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -118,9 +118,11 @@
             switch (this) {
                 case UNINITIALIZED:
                 case INITIALIZED:
-                case DOZE_REQUEST_PULSE:
                     return parameters.shouldControlScreenOff() ? Display.STATE_ON
                             : Display.STATE_OFF;
+                case DOZE_REQUEST_PULSE:
+                    return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF
+                            : Display.STATE_ON;
                 case DOZE_AOD_PAUSED:
                 case DOZE:
                     return Display.STATE_OFF;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 239109a..c8720e4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -288,7 +288,7 @@
         for (TriggerSensor triggerSensor : mTriggerSensors) {
             triggerSensor.setListening(false);
         }
-        mProximitySensor.pause();
+        mProximitySensor.destroy();
 
         mDevicePostureController.removeCallback(mDevicePostureCallback);
         mAuthController.removeCallback(mAuthControllerCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 68b74bd..8bff3ba 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -228,6 +228,7 @@
     @Override
     public void destroy() {
         mDozeSensors.destroy();
+        mProxCheck.destroy();
     }
 
     private void onNotification(Runnable onPulseSuppressedListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
new file mode 100644
index 0000000..02a8b39a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -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.dreams;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+
+/**
+ * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by
+ * {@link DreamOverlayStatusBarView}.
+ */
+public class DreamOverlayDotImageView extends AlphaOptimizedImageView {
+    private final @ColorInt int mDotColor;
+
+    public DreamOverlayDotImageView(Context context) {
+        this(context, null);
+    }
+
+    public DreamOverlayDotImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DreamOverlayDotImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DreamOverlayDotImageView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
+                R.styleable.DreamOverlayDotImageView, 0, 0);
+
+        try {
+            mDotColor = a.getColor(R.styleable.DreamOverlayDotImageView_dotColor, Color.WHITE);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setImageDrawable(new DotDrawable(mDotColor));
+    }
+
+    private static class DotDrawable extends Drawable {
+        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        private Bitmap mDotBitmap;
+        private final Rect mBounds = new Rect();
+        private final @ColorInt int mDotColor;
+
+        DotDrawable(@ColorInt int color) {
+            mDotColor = color;
+        }
+
+        @Override
+        public void draw(@NonNull Canvas canvas) {
+            if (mBounds.isEmpty()) {
+                return;
+            }
+
+            if (mDotBitmap == null) {
+                mDotBitmap = createBitmap(mBounds.width(), mBounds.height());
+            }
+
+            canvas.drawBitmap(mDotBitmap, null, mBounds, mPaint);
+        }
+
+        @Override
+        protected void onBoundsChange(Rect bounds) {
+            super.onBoundsChange(bounds);
+            mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+            // Make sure to regenerate the dot bitmap when the bounds change.
+            mDotBitmap = null;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        private Bitmap createBitmap(int width, int height) {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            paint.setColor(mDotColor);
+            canvas.drawCircle(width / 2.f, height / 2.f, Math.min(width, height) / 2.f, paint);
+            return bitmap;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 4dacf65..338a8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -132,6 +132,7 @@
     public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
         setCurrentState(Lifecycle.State.STARTED);
         mExecutor.execute(() -> {
+            mStateController.setShouldShowComplications(shouldShowComplications());
             addOverlayWindowLocked(layoutParams);
             setCurrentState(Lifecycle.State.RESUMED);
             mStateController.setOverlayActive(true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index bc5a52a..fc71e2f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.dreams;
 
+import android.service.dreams.DreamService;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -84,6 +85,8 @@
     @Complication.ComplicationType
     private int mAvailableComplicationTypes = Complication.COMPLICATION_TYPE_NONE;
 
+    private boolean mShouldShowComplications = DreamService.DEFAULT_SHOW_COMPLICATIONS;
+
     private final Collection<Complication> mComplications = new HashSet();
 
     @VisibleForTesting
@@ -131,7 +134,12 @@
                 .filter(complication -> {
                     @Complication.ComplicationType
                     final int requiredTypes = complication.getRequiredTypeAvailability();
-                    return (requiredTypes & getAvailableComplicationTypes()) == requiredTypes;
+                    // If it should show complications, show ones whose required types are
+                    // available. Otherwise, only show ones that don't require types.
+                    if (mShouldShowComplications) {
+                        return (requiredTypes & getAvailableComplicationTypes()) == requiredTypes;
+                    }
+                    return requiredTypes == Complication.COMPLICATION_TYPE_NONE;
                 })
                 .collect(Collectors.toCollection(HashSet::new))
                 : mComplications);
@@ -221,7 +229,24 @@
     public void setAvailableComplicationTypes(@Complication.ComplicationType int types) {
         mExecutor.execute(() -> {
             mAvailableComplicationTypes = types;
-            mCallbacks.forEach(callback -> callback.onAvailableComplicationTypesChanged());
+            mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
+        });
+    }
+
+    /**
+     * Returns whether the dream overlay should show complications.
+     */
+    public boolean getShouldShowComplications() {
+        return mShouldShowComplications;
+    }
+
+    /**
+     * Sets whether the dream overlay should show complications.
+     */
+    public void setShouldShowComplications(boolean shouldShowComplications) {
+        mExecutor.execute(() -> {
+            mShouldShowComplications = shouldShowComplications;
+            mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
         });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 9847ef6..2d96920 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -25,17 +25,13 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.systemui.R;
-import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 
 /**
  * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
- * dream. The status bar includes status icons such as battery and wifi.
+ * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
  */
-public class DreamOverlayStatusBarView extends ConstraintLayout implements
-        BatteryStateChangeCallback {
+public class DreamOverlayStatusBarView extends ConstraintLayout {
 
-    private BatteryMeterView mBatteryView;
     private ImageView mWifiStatusView;
 
     public DreamOverlayStatusBarView(Context context) {
@@ -59,20 +55,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
-                "R.id.dream_overlay_battery must not be null");
         mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
                 "R.id.dream_overlay_wifi_status must not be null");
-
-        mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
-    }
-
-    /**
-     * Whether to show the battery percent text next to the battery status icons.
-     * @param show True if the battery percent text should be shown.
-     */
-    void showBatteryPercentText(boolean show) {
-        mBatteryView.setForceShowPercent(show);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 5674b9f..ed82ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -17,24 +17,19 @@
 package com.android.systemui.dreams;
 
 import android.annotation.IntDef;
-import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 
-import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
-import com.android.systemui.dreams.dagger.DreamOverlayModule;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /**
  * View controller for {@link DreamOverlayStatusBarView}.
@@ -52,21 +47,7 @@
     private static final int WIFI_STATUS_UNAVAILABLE = 1;
     private static final int WIFI_STATUS_AVAILABLE = 2;
 
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
-            BATTERY_STATUS_UNKNOWN,
-            BATTERY_STATUS_NOT_CHARGING,
-            BATTERY_STATUS_CHARGING
-    })
-    private @interface BatteryStatus {}
-    private static final int BATTERY_STATUS_UNKNOWN = 0;
-    private static final int BATTERY_STATUS_NOT_CHARGING = 1;
-    private static final int BATTERY_STATUS_CHARGING = 2;
-
-    private final BatteryController mBatteryController;
-    private final BatteryMeterViewController mBatteryMeterViewController;
     private final ConnectivityManager mConnectivityManager;
-    private final boolean mShowPercentAvailable;
 
     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
             .clearCapabilities()
@@ -91,43 +72,18 @@
         }
     };
 
-    private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
-            new BatteryController.BatteryStateChangeCallback() {
-                @Override
-                public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-                    DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
-                }
-            };
-
     private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
-    private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
 
     @Inject
     public DreamOverlayStatusBarViewController(
-            Context context,
             DreamOverlayStatusBarView view,
-            BatteryController batteryController,
-            @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
-                    BatteryMeterViewController batteryMeterViewController,
             ConnectivityManager connectivityManager) {
         super(view);
-        mBatteryController = batteryController;
-        mBatteryMeterViewController = batteryMeterViewController;
         mConnectivityManager = connectivityManager;
-
-        mShowPercentAvailable = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_battery_percentage_setting_available);
-    }
-
-    @Override
-    protected void onInit() {
-        super.onInit();
-        mBatteryMeterViewController.init();
     }
 
     @Override
     protected void onViewAttached() {
-        mBatteryController.addCallback(mBatteryStateChangeCallback);
         mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
 
         NetworkCapabilities capabilities =
@@ -140,7 +96,6 @@
 
     @Override
     protected void onViewDetached() {
-        mBatteryController.removeCallback(mBatteryStateChangeCallback);
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
     }
 
@@ -155,18 +110,4 @@
             mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
         }
     }
-
-    /**
-     * The battery level has changed. Update the battery status icon as appropriate.
-     * @param charging Whether the battery is currently charging.
-     */
-    private void onBatteryLevelChanged(boolean charging) {
-        final int newBatteryStatus =
-                charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
-        if (mBatteryStatus != newBatteryStatus) {
-            mBatteryStatus = newBatteryStatus;
-            mView.showBatteryPercentText(
-                    mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
-        }
-    }
 }
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 4eb5cb9..839a05e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -16,9 +16,7 @@
 
 package com.android.systemui.dreams.dagger;
 
-import android.content.ContentResolver;
 import android.content.res.Resources;
-import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -28,15 +26,9 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.systemui.R;
-import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayContainerView;
 import com.android.systemui.dreams.DreamOverlayStatusBarView;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
 
 import javax.inject.Named;
 
@@ -47,9 +39,6 @@
 /** Dagger module for {@link DreamOverlayComponent}. */
 @Module
 public abstract class DreamOverlayModule {
-    private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
-    public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
-            "dream_overlay_battery_controller";
     public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
     public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
     public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
@@ -86,37 +75,6 @@
     /** */
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
-    @Named(DREAM_OVERLAY_BATTERY_VIEW)
-    static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
-        return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
-                "R.id.battery must not be null");
-    }
-
-    /** */
-    @Provides
-    @DreamOverlayComponent.DreamOverlayScope
-    @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
-    static BatteryMeterViewController providesBatteryMeterViewController(
-            @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
-            ConfigurationController configurationController,
-            TunerService tunerService,
-            BroadcastDispatcher broadcastDispatcher,
-            @Main Handler mainHandler,
-            ContentResolver contentResolver,
-            BatteryController batteryController) {
-        return new BatteryMeterViewController(
-                batteryMeterView,
-                configurationController,
-                tunerService,
-                broadcastDispatcher,
-                mainHandler,
-                contentResolver,
-                batteryController);
-    }
-
-    /** */
-    @Provides
-    @DreamOverlayComponent.DreamOverlayScope
     @Named(MAX_BURN_IN_OFFSET)
     static int providesMaxBurnInOffset(@Main Resources resources) {
         return resources.getDimensionPixelSize(R.dimen.default_burn_in_prevention_offset);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 55e48a7..5487c42 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -111,7 +111,7 @@
     public static final ResourceBooleanFlag QS_USER_DETAIL_SHORTCUT =
             new ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut);
 
-    public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, false);
+    public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, true);
 
     public static final BooleanFlag NEW_HEADER = new BooleanFlag(505, false);
     public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
@@ -145,7 +145,7 @@
     // 900 - media
     public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
     public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
-    public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
+    public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
     public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
     public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index f0371fc..e3886cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -27,10 +27,10 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -76,8 +76,10 @@
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -109,11 +111,11 @@
 import com.android.systemui.MultiListLayout;
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.scrim.ScrimDrawable;
@@ -121,7 +123,6 @@
 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;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -196,7 +197,6 @@
     private final TelecomManager mTelecomManager;
     private final MetricsLogger mMetricsLogger;
     private final UiEventLogger mUiEventLogger;
-    private final SysUiState mSysUiState;
 
     // Used for RingerModeTracker
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -237,7 +237,6 @@
     protected Handler mMainHandler;
     private int mSmallestScreenWidthDp;
     private final Optional<StatusBar> mStatusBarOptional;
-    private final SystemUIDialogManager mDialogManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
 
@@ -343,13 +342,11 @@
             @Background Executor backgroundExecutor,
             UiEventLogger uiEventLogger,
             RingerModeTracker ringerModeTracker,
-            SysUiState sysUiState,
             @Main Handler handler,
             PackageManager packageManager,
             Optional<StatusBar> statusBarOptional,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            DialogLaunchAnimator dialogLaunchAnimator,
-            SystemUIDialogManager dialogManager) {
+            DialogLaunchAnimator dialogLaunchAnimator) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -375,13 +372,11 @@
         mIWindowManager = iWindowManager;
         mBackgroundExecutor = backgroundExecutor;
         mRingerModeTracker = ringerModeTracker;
-        mSysUiState = sysUiState;
         mMainHandler = handler;
         mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
         mStatusBarOptional = statusBarOptional;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDialogLaunchAnimator = dialogLaunchAnimator;
-        mDialogManager = dialogManager;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -678,11 +673,10 @@
 
         ActionsDialogLite dialog = new ActionsDialogLite(mContext,
                 com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
-                mAdapter, mOverflowAdapter, mSysuiColorExtractor,
-                mStatusBarService, mNotificationShadeWindowController,
-                mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
-                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils,
-                mDialogManager);
+                mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
+                mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
+                mPowerAdapter, mUiEventLogger, mStatusBarOptional, mKeyguardUpdateMonitor,
+                mLockPatternUtils);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -2161,7 +2155,6 @@
         private boolean mKeyguardShowing;
         protected float mScrimAlpha;
         protected final NotificationShadeWindowController mNotificationShadeWindowController;
-        protected final SysUiState mSysUiState;
         private ListPopupWindow mOverflowPopup;
         private Dialog mPowerOptionsDialog;
         protected final Runnable mOnRefreshCallback;
@@ -2170,6 +2163,7 @@
         private Optional<StatusBar> mStatusBarOptional;
         private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
         private LockPatternUtils mLockPatternUtils;
+        private float mWindowDimAmount;
 
         protected ViewGroup mContainer;
 
@@ -2221,15 +2215,13 @@
                 MyOverflowAdapter overflowAdapter,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
-                SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
+                Runnable onRefreshCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
-                Optional<StatusBar> statusBarOptional,
-                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
-                SystemUIDialogManager systemUiDialogManager) {
+                Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor,
+                LockPatternUtils lockPatternUtils) {
             // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
             // dismiss this dialog when the device is locked.
-            super(context, themeRes, false /* dismissOnDeviceLock */,
-                    systemUiDialogManager);
+            super(context, themeRes, false /* dismissOnDeviceLock */);
             mContext = context;
             mAdapter = adapter;
             mOverflowAdapter = overflowAdapter;
@@ -2237,7 +2229,6 @@
             mColorExtractor = sysuiColorExtractor;
             mStatusBarService = statusBarService;
             mNotificationShadeWindowController = notificationShadeWindowController;
-            mSysUiState = sysuiState;
             mOnRefreshCallback = onRefreshCallback;
             mKeyguardShowing = keyguardShowing;
             mUiEventLogger = uiEventLogger;
@@ -2251,6 +2242,7 @@
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             initializeLayout();
+            mWindowDimAmount = getWindow().getAttributes().dimAmount;
         }
 
         @Override
@@ -2457,8 +2449,96 @@
         public void show() {
             super.show();
             mNotificationShadeWindowController.setRequestTopUi(true, TAG);
-            mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
-                    .commitUpdate(mContext.getDisplayId());
+
+            // By default this dialog windowAnimationStyle is null, and therefore windowAnimations
+            // should be equal to 0 which means we need to animate the dialog in-window. If it's not
+            // equal to 0, it means it has been overridden to animate (e.g. by the
+            // DialogLaunchAnimator) so we don't run the animation.
+            boolean shouldAnimateInWindow = getWindow().getAttributes().windowAnimations == 0;
+            if (shouldAnimateInWindow) {
+                startAnimation(true /* isEnter */, null /* then */);
+
+                // Override the dialog dismiss so that we can animate in-window before dismissing
+                // the dialog.
+                setDismissOverride(() -> {
+                    startAnimation(false /* isEnter */, /* then */ () -> {
+                        setDismissOverride(null);
+
+                        // Hide then dismiss to instantly dismiss.
+                        hide();
+                        dismiss();
+                    });
+                });
+            }
+        }
+
+        /** Run either the enter or exit animation, then run {@code then}. */
+        private void startAnimation(boolean isEnter, @Nullable Runnable then) {
+            ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+
+            // Note: these specs should be the same as in popup_enter_material and
+            // popup_exit_material.
+            float translationPx;
+            Resources resources = getContext().getResources();
+            if (isEnter) {
+                translationPx = resources.getDimension(R.dimen.popup_enter_animation_from_y_delta);
+                animator.setInterpolator(Interpolators.STANDARD);
+                animator.setDuration(resources.getInteger(R.integer.config_activityDefaultDur));
+            } else {
+                translationPx = resources.getDimension(R.dimen.popup_exit_animation_to_y_delta);
+                animator.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+                animator.setDuration(resources.getInteger(R.integer.config_activityShortDur));
+            }
+
+            Window window = getWindow();
+            int rotation = window.getWindowManager().getDefaultDisplay().getRotation();
+
+            animator.addUpdateListener(valueAnimator -> {
+                float progress = (float) valueAnimator.getAnimatedValue();
+
+                float alpha = isEnter ? progress : 1 - progress;
+                mGlobalActionsLayout.setAlpha(alpha);
+                window.setDimAmount(mWindowDimAmount * alpha);
+
+                // TODO(b/213872558): Support devices that don't have their power button on the
+                // right.
+                float translation =
+                        isEnter ? translationPx * (1 - progress) : translationPx * progress;
+                switch (rotation) {
+                    case Surface.ROTATION_0:
+                        mGlobalActionsLayout.setTranslationX(translation);
+                        break;
+                    case Surface.ROTATION_90:
+                        mGlobalActionsLayout.setTranslationY(-translation);
+                        break;
+                    case Surface.ROTATION_180:
+                        mGlobalActionsLayout.setTranslationX(-translation);
+                        break;
+                    case Surface.ROTATION_270:
+                        mGlobalActionsLayout.setTranslationY(translation);
+                        break;
+                }
+            });
+
+            animator.addListener(new AnimatorListenerAdapter() {
+                private int mPreviousLayerType;
+
+                @Override
+                public void onAnimationStart(Animator animation, boolean isReverse) {
+                    mPreviousLayerType = mGlobalActionsLayout.getLayerType();
+                    mGlobalActionsLayout.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mGlobalActionsLayout.setLayerType(mPreviousLayerType, null);
+                    if (then != null) {
+                        then.run();
+                    }
+                }
+            });
+
+            animator.start();
         }
 
         @Override
@@ -2467,9 +2547,6 @@
             dismissPowerOptions();
 
             mNotificationShadeWindowController.setRequestTopUi(false, TAG);
-            mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
-                    .commitUpdate(mContext.getDisplayId());
-
             super.dismiss();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
deleted file mode 100644
index fa00dbb..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle
-
-import android.annotation.IntDef
-import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
-import android.hardware.SensorManager
-import android.util.Log
-import com.android.systemui.util.sensors.AsyncSensorManager
-import javax.inject.Inject
-
-/**
- * Monitors ambient light signals, applies a debouncing algorithm, and produces the current
- * ambient light mode.
- *
- * @property algorithm the debounce algorithm which transforms light sensor events into an
- * ambient light mode.
- * @property sensorManager the sensor manager used to register sensor event updates.
- */
-class AmbientLightModeMonitor @Inject constructor(
-    private val algorithm: DebounceAlgorithm,
-    private val sensorManager: AsyncSensorManager
-) {
-    companion object {
-        private const val TAG = "AmbientLightModeMonitor"
-        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
-
-        const val AMBIENT_LIGHT_MODE_LIGHT = 0
-        const val AMBIENT_LIGHT_MODE_DARK = 1
-        const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
-    }
-
-    // Light sensor used to detect ambient lighting conditions.
-    private val lightSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
-
-    // Represents all ambient light modes.
-    @Retention(AnnotationRetention.SOURCE)
-    @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
-    annotation class AmbientLightMode
-
-    /**
-     * Start monitoring the current ambient light mode.
-     *
-     * @param callback callback that gets triggered when the ambient light mode changes.
-     */
-    fun start(callback: Callback) {
-        if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
-
-        if (lightSensor == null) {
-            if (DEBUG) Log.w(TAG, "light sensor not available")
-            return
-        }
-
-        algorithm.start(callback)
-        sensorManager.registerListener(mSensorEventListener, lightSensor,
-                SensorManager.SENSOR_DELAY_NORMAL)
-    }
-
-    /**
-     * Stop monitoring the current ambient light mode.
-     */
-    fun stop() {
-        if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
-
-        algorithm.stop()
-        sensorManager.unregisterListener(mSensorEventListener)
-    }
-
-    private val mSensorEventListener: SensorEventListener = object : SensorEventListener {
-        override fun onSensorChanged(event: SensorEvent) {
-            if (event.values.isEmpty()) {
-                if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
-                return
-            }
-
-            algorithm.onUpdateLightSensorEvent(event.values[0])
-        }
-
-        override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
-            // Do nothing.
-        }
-    }
-
-    /**
-     * Interface of the ambient light mode callback, which gets triggered when the mode changes.
-     */
-    interface Callback {
-        fun onChange(@AmbientLightMode mode: Int)
-    }
-
-    /**
-     * Interface of the algorithm that transforms light sensor events to an ambient light mode.
-     */
-    interface DebounceAlgorithm {
-        // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
-        fun start(callback: Callback?)
-        fun stop()
-        fun onUpdateLightSensorEvent(value: Float)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
deleted file mode 100644
index 7d79279..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * {@link IdleHostView} houses a surface to be displayed when the device idle.
- */
-public class IdleHostView extends FrameLayout {
-    public IdleHostView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public IdleHostView(@NonNull Context context,
-            @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public IdleHostView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
deleted file mode 100644
index 624d01f..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle;
-
-import static com.android.systemui.communal.dagger.CommunalModule.IDLE_VIEW;
-
-import android.annotation.IntDef;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ViewController;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
-
-/**
- * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
- */
-public class IdleHostViewController extends ViewController<IdleHostView> {
-    private static final String TAG = "IdleHostViewController";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
-            STATE_LOW_LIGHT})
-    public @interface State {}
-
-    // Set at construction to indicate idle mode is available.
-    private static final int STATE_IDLE_MODE_ENABLED = 1 << 0;
-
-    // Set when keyguard is present, even below a dream or AOD. AKA the device is locked.
-    private static final int STATE_KEYGUARD_SHOWING = 1 << 1;
-
-    // Set when the device has entered a dozing / low power state.
-    private static final int STATE_DOZING = 1 << 2;
-
-    // Set when the device has entered a dreaming state, which includes dozing.
-    private static final int STATE_DREAMING = 1 << 3;
-
-    // Set when the device is in a low light environment.
-    private static final int STATE_LOW_LIGHT = 1 << 4;
-
-    // The aggregate current state.
-    private int mState;
-    private boolean mIsMonitoringLowLight;
-    private boolean mIsMonitoringDream;
-
-    private final BroadcastDispatcher mBroadcastDispatcher;
-
-    private final PowerManager mPowerManager;
-
-    // Keyguard state controller for monitoring keyguard show state.
-    private final KeyguardStateController mKeyguardStateController;
-
-    // Status bar state controller for monitoring when the device is dozing.
-    private final StatusBarStateController mStatusBarStateController;
-
-    // Intent filter for receiving dream broadcasts.
-    private IntentFilter mDreamIntentFilter;
-
-    // Monitor for the current ambient light mode. Used to trigger / exit low-light mode.
-    private final AmbientLightModeMonitor mAmbientLightModeMonitor;
-
-    private final KeyguardStateController.Callback mKeyguardCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardShowingChanged() {
-                    setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
-                }
-            };
-
-    private final StatusBarStateController.StateListener mStatusBarCallback =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onDozingChanged(boolean isDozing) {
-                    setState(STATE_DOZING, isDozing);
-                }
-            };
-
-    private final BroadcastReceiver mDreamStateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
-                setState(STATE_DREAMING, true);
-            } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
-                setState(STATE_DREAMING, false);
-            }
-        }
-    };
-
-    private final AmbientLightModeMonitor.Callback mAmbientLightModeCallback =
-            mode -> {
-                boolean shouldBeLowLight;
-                switch (mode) {
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED:
-                        return;
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT:
-                        shouldBeLowLight = false;
-                        break;
-                    case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK:
-                        shouldBeLowLight = true;
-                        break;
-                    default:
-                        Log.w(TAG, "invalid ambient light mode");
-                        return;
-                }
-
-                if (DEBUG) Log.d(TAG, "ambient light mode changed to " + mode);
-
-                final boolean isLowLight = getState(STATE_LOW_LIGHT);
-                if (shouldBeLowLight != isLowLight) {
-                    setState(STATE_LOW_LIGHT, shouldBeLowLight);
-                }
-            };
-
-    final Provider<View> mIdleViewProvider;
-
-    @Inject
-    protected IdleHostViewController(
-            BroadcastDispatcher broadcastDispatcher,
-            PowerManager powerManager,
-            IdleHostView view,
-            @Main Resources resources,
-            @Named(IDLE_VIEW) Provider<View> idleViewProvider,
-            KeyguardStateController keyguardStateController,
-            StatusBarStateController statusBarStateController,
-            AmbientLightModeMonitor ambientLightModeMonitor) {
-        super(view);
-        mBroadcastDispatcher = broadcastDispatcher;
-        mPowerManager = powerManager;
-        mIdleViewProvider = idleViewProvider;
-        mKeyguardStateController = keyguardStateController;
-        mStatusBarStateController = statusBarStateController;
-        mAmbientLightModeMonitor = ambientLightModeMonitor;
-
-        mState = STATE_KEYGUARD_SHOWING;
-
-        final boolean enabled = resources.getBoolean(R.bool.config_enableIdleMode);
-        if (enabled) {
-            mState |= STATE_IDLE_MODE_ENABLED;
-        }
-
-        setState(mState, true);
-
-        if (DEBUG) {
-            Log.d(TAG, "initial state:" + mState + " enabled:" + enabled);
-        }
-    }
-
-    @Override
-    public void init() {
-        super.init();
-
-        setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
-        setState(STATE_DOZING, mStatusBarStateController.isDozing());
-    }
-
-    private void setState(@State int state, boolean active) {
-        final int oldState = mState;
-
-        if (active) {
-            mState |= state;
-        } else {
-            mState &= ~state;
-        }
-
-        if (oldState == mState) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "set " + getStateName(state) + " to " + active);
-            logCurrentState();
-        }
-
-        final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
-                && getState(STATE_KEYGUARD_SHOWING);
-
-        enableDreamMonitoring(inCommunalMode);
-        enableLowLightMonitoring(inCommunalMode);
-
-        if (state == STATE_LOW_LIGHT) {
-            enableLowLightMode(inCommunalMode && active);
-        }
-    }
-
-    private void enableDreamMonitoring(boolean enable) {
-        if (mIsMonitoringDream == enable) {
-            return;
-        }
-
-        mIsMonitoringDream = enable;
-
-        if (DEBUG) {
-            Log.d(TAG, (enable ? "enable" : "disable") + " dream monitoring");
-        }
-
-        if (mDreamIntentFilter == null) {
-            mDreamIntentFilter = new IntentFilter();
-            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
-            mDreamIntentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-        }
-
-        if (enable) {
-            mBroadcastDispatcher.registerReceiver(mDreamStateReceiver, mDreamIntentFilter);
-        } else {
-            mBroadcastDispatcher.unregisterReceiver(mDreamStateReceiver);
-        }
-    }
-
-    private void enableLowLightMonitoring(boolean enable) {
-        if (enable == mIsMonitoringLowLight) {
-            return;
-        }
-
-        mIsMonitoringLowLight = enable;
-
-        if (mIsMonitoringLowLight) {
-            if (DEBUG) Log.d(TAG, "enable low light monitoring");
-            mAmbientLightModeMonitor.start(mAmbientLightModeCallback);
-        } else {
-            if (DEBUG) Log.d(TAG, "disable low light monitoring");
-            mAmbientLightModeMonitor.stop();
-        }
-    }
-
-    private void enableLowLightMode(boolean enable) {
-        if (enable == getState(STATE_DOZING)) {
-            return;
-        }
-
-        if (enable) {
-            if (DEBUG) Log.d(TAG, "enter low light, start dozing");
-
-            mPowerManager.goToSleep(
-                    SystemClock.uptimeMillis(),
-                    PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
-        } else {
-            if (DEBUG) Log.d(TAG, "exit low light, stop dozing");
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(),
-                    PowerManager.WAKE_REASON_APPLICATION, "Exit low light condition");
-        }
-    }
-
-    @Override
-    protected void onViewAttached() {
-        if (DEBUG) {
-            Log.d(TAG, "onViewAttached");
-        }
-
-        mKeyguardStateController.addCallback(mKeyguardCallback);
-        mStatusBarStateController.addCallback(mStatusBarCallback);
-    }
-
-    @Override
-    protected void onViewDetached() {
-        mKeyguardStateController.removeCallback(mKeyguardCallback);
-        mStatusBarStateController.removeCallback(mStatusBarCallback);
-    }
-
-    private String getStateName(@State int state) {
-        switch (state) {
-            case STATE_IDLE_MODE_ENABLED:
-                return "STATE_IDLE_MODE_ENABLED";
-            case STATE_KEYGUARD_SHOWING:
-                return "STATE_KEYGUARD_SHOWING";
-            case STATE_DOZING:
-                return "STATE_DOZING";
-            case STATE_DREAMING:
-                return "STATE_DREAMING";
-            case STATE_LOW_LIGHT:
-                return "STATE_LOW_LIGHT";
-            default:
-                return "STATE_UNKNOWN";
-        }
-    }
-
-    private boolean getState(@State int state) {
-        return getState(state, mState);
-    }
-
-    private boolean getState(@State int state, int oldState) {
-        return (oldState & state) == state;
-    }
-
-    private String getStateLog(@State int state) {
-        return getStateName(state) + " = " + getState(state);
-    }
-
-    private void logCurrentState() {
-        Log.d(TAG, "current state: {\n"
-                + "\t" + getStateLog(STATE_IDLE_MODE_ENABLED) + "\n"
-                + "\t" + getStateLog(STATE_KEYGUARD_SHOWING) + "\n"
-                + "\t" + getStateLog(STATE_DOZING) + "\n"
-                + "\t" + getStateLog(STATE_DREAMING) + "\n"
-                + "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
-                + "}");
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java b/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
deleted file mode 100644
index be0a378..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle;
-
-import com.android.systemui.dagger.qualifiers.DisplayId;
-import com.android.systemui.shared.system.InputMonitorCompat;
-
-import javax.inject.Inject;
-
-/**
- * {@link InputMonitorFactory} generates instances of {@link InputMonitorCompat}.
- */
-public class InputMonitorFactory {
-    private final int mDisplayId;
-
-    @Inject
-    public InputMonitorFactory(@DisplayId int displayId) {
-        mDisplayId = displayId;
-    }
-
-    /**
-     * Generates a new {@link InputMonitorCompat}.
-     *
-     * @param identifier Identifier to generate monitor with.
-     * @return A {@link InputMonitorCompat} instance.
-     */
-    public InputMonitorCompat getInputMonitor(String identifier) {
-        return new InputMonitorCompat(identifier, mDisplayId);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt b/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
deleted file mode 100644
index 7b06b96..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/LightSensorEventsDebounceAlgorithm.kt
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle
-
-import android.content.res.Resources
-import android.util.Log
-import com.android.systemui.R
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.util.concurrency.DelayableExecutor
-import javax.inject.Inject
-
-/**
- * An algorithm that receives light sensor events, debounces the signals, and transforms into an
- * ambient light mode: light, dark, or undecided.
- *
- * More about the algorithm at go/titan-light-sensor-debouncer.
- */
-class LightSensorEventsDebounceAlgorithm @Inject constructor(
-    @Main private val executor: DelayableExecutor,
-    @Main resources: Resources
-) : AmbientLightModeMonitor.DebounceAlgorithm {
-    companion object {
-        private const val TAG = "LightDebounceAlgorithm"
-        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
-    }
-
-    // The ambient mode is considered light mode when the light sensor value increases exceeding
-    // this value.
-    private val lightModeThreshold =
-            resources.getInteger(R.integer.config_ambientLightModeThreshold)
-
-    // The ambient mode is considered dark mode when the light sensor value drops below this
-    // value.
-    private val darkModeThreshold = resources.getInteger(R.integer.config_ambientDarkModeThreshold)
-
-    // Each sample for calculating light mode contains light sensor events collected for this
-    // duration of time in milliseconds.
-    private val lightSamplingSpanMillis =
-            resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis)
-
-    // Each sample for calculating dark mode contains light sensor events collected for this
-    // duration of time in milliseconds.
-    private val darkSamplingSpanMillis =
-            resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis)
-
-    // The calculations for light mode is performed at this frequency in milliseconds.
-    private val lightSamplingFrequencyMillis =
-            resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis)
-
-    // The calculations for dark mode is performed at this frequency in milliseconds.
-    private val darkSamplingFrequencyMillis =
-            resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis)
-
-    // Registered callback, which gets triggered when the ambient light mode changes.
-    private var callback: AmbientLightModeMonitor.Callback? = null
-
-    // Queue of bundles used for calculating [isLightMode], ordered from oldest to latest.
-    val bundlesQueueLightMode = ArrayList<ArrayList<Float>>()
-
-    // Queue of bundles used for calculating [isDarkMode], ordered from oldest to latest
-    val bundlesQueueDarkMode = ArrayList<ArrayList<Float>>()
-
-    // The current ambient light mode.
-    @AmbientLightModeMonitor.AmbientLightMode
-    var mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "ambient light mode changed to $value")
-
-            callback?.onChange(value)
-        }
-
-    // The latest claim of whether it should be light mode.
-    var isLightMode = false
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "isLightMode: $value")
-
-            mode = when {
-                isDarkMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-            }
-        }
-
-    // The latest claim of whether it should be dark mode.
-    var isDarkMode = false
-        set(value) {
-            if (field == value) return
-            field = value
-
-            if (DEBUG) Log.d(TAG, "isDarkMode: $value")
-
-            mode = when {
-                value -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-                isLightMode -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-                else -> AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-            }
-        }
-
-    // The latest average of the light mode bundle.
-    var bundleAverageLightMode = 0.0
-        set(value) {
-            field = value
-
-            if (DEBUG) Log.d(TAG, "light mode average: $value")
-
-            isLightMode = value > lightModeThreshold
-        }
-
-    // The latest average of the dark mode bundle.
-    var bundleAverageDarkMode = 0.0
-        set(value) {
-            field = value
-
-            if (DEBUG) Log.d(TAG, "dark mode average: $value")
-
-            isDarkMode = value < darkModeThreshold
-        }
-
-    // The latest bundle for calculating light mode claim.
-    var bundleLightMode = ArrayList<Float>()
-        set(value) {
-            field = value
-
-            val average = value.average()
-
-            if (!average.isNaN()) {
-                bundleAverageLightMode = average
-            }
-        }
-
-    // The latest bundle for calculating dark mode claim.
-    var bundleDarkMode = ArrayList<Float>()
-        set(value) {
-            field = value
-
-            val average = value.average()
-
-            if (!average.isNaN()) {
-                bundleAverageDarkMode = average
-            }
-        }
-
-    // The latest light level from light sensor event updates.
-    var lightSensorLevel = 0.0f
-        set(value) {
-            field = value
-
-            bundlesQueueLightMode.forEach { bundle -> bundle.add(value) }
-            bundlesQueueDarkMode.forEach { bundle -> bundle.add(value) }
-        }
-
-    // Creates a new bundle that collects light sensor events for calculating the light mode claim,
-    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
-    // [LIGHT_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
-    // [LIGHT_SAMPLING_FREQUENCY_MILLIS].
-    val enqueueLightModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (DEBUG) Log.d(TAG, "enqueueing a light mode bundle")
-
-            bundlesQueueLightMode.add(ArrayList())
-
-            executor.executeDelayed(dequeueLightModeBundle, lightSamplingSpanMillis.toLong())
-            executor.executeDelayed(this, lightSamplingFrequencyMillis.toLong())
-        }
-    }
-
-    // Creates a new bundle that collects light sensor events for calculating the dark mode claim,
-    // and adds it to the end of the queue. It schedules a call to dequeue this bundle after
-    // [DARK_SAMPLING_SPAN_MILLIS]. Once started, it also repeatedly calls itself at
-    // [DARK_SAMPLING_FREQUENCY_MILLIS].
-    val enqueueDarkModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (DEBUG) Log.d(TAG, "enqueueing a dark mode bundle")
-
-            bundlesQueueDarkMode.add(ArrayList())
-
-            executor.executeDelayed(dequeueDarkModeBundle, darkSamplingSpanMillis.toLong())
-            executor.executeDelayed(this, darkSamplingFrequencyMillis.toLong())
-        }
-    }
-
-    // Collects the oldest bundle from the light mode bundles queue, and as a result triggering a
-    // calculation of the light mode claim.
-    val dequeueLightModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (bundlesQueueLightMode.isEmpty()) return
-
-            bundleLightMode = bundlesQueueLightMode.removeAt(0)
-
-            if (DEBUG) Log.d(TAG, "dequeued a light mode bundle of size ${bundleLightMode.size}")
-        }
-    }
-
-    // Collects the oldest bundle from the dark mode bundles queue, and as a result triggering a
-    // calculation of the dark mode claim.
-    val dequeueDarkModeBundle: Runnable = object : Runnable {
-        override fun run() {
-            if (bundlesQueueDarkMode.isEmpty()) return
-
-            bundleDarkMode = bundlesQueueDarkMode.removeAt(0)
-
-            if (DEBUG) Log.d(TAG, "dequeued a dark mode bundle of size ${bundleDarkMode.size}")
-        }
-    }
-
-    /**
-     * Start the algorithm.
-     *
-     * @param callback callback that gets triggered when the ambient light mode changes.
-     */
-    override fun start(callback: AmbientLightModeMonitor.Callback?) {
-        if (DEBUG) Log.d(TAG, "start algorithm")
-
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "callback is null")
-            return
-        }
-
-        if (this.callback != null) {
-            if (DEBUG) Log.w(TAG, "already started")
-            return
-        }
-
-        this.callback = callback
-
-        executor.execute(enqueueLightModeBundle)
-        executor.execute(enqueueDarkModeBundle)
-    }
-
-    /**
-     * Stop the algorithm.
-     */
-    override fun stop() {
-        if (DEBUG) Log.d(TAG, "stop algorithm")
-
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "haven't started")
-            return
-        }
-
-        callback = null
-
-        // Resets bundle queues.
-        bundlesQueueLightMode.clear()
-        bundlesQueueDarkMode.clear()
-
-        // Resets ambient light mode.
-        mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED
-    }
-
-    /**
-     * Update the light sensor event value.
-     *
-     * @param value light sensor update value.
-     */
-    override fun onUpdateLightSensorEvent(value: Float) {
-        if (callback == null) {
-            if (DEBUG) Log.w(TAG, "ignore light sensor event because algorithm not started")
-            return
-        }
-
-        lightSensorLevel = value
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java b/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java
deleted file mode 100644
index 9754b1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle.dagger;
-
-import com.android.systemui.idle.IdleHostView;
-import com.android.systemui.idle.IdleHostViewController;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Subcomponent for working with {@link IdleHostView}.
- */
-@Subcomponent
-public interface IdleViewComponent {
-    /** Simple factory for {@link Factory}. */
-    @Subcomponent.Factory
-    interface Factory {
-        IdleViewComponent build(@BindsInstance IdleHostView idleHostView);
-    }
-
-    /** Builds a {@link IdleHostViewController}. */
-    IdleHostViewController getIdleHostViewController();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index b96eee7..88555ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -102,7 +102,7 @@
             "persist.wm.enable_remote_keyguard_animation";
 
     private static final int sEnableRemoteKeyguardAnimation =
-            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2);
+            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
 
     /**
      * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java
new file mode 100644
index 0000000..0b15f4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightClockController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.lowlightclock;
+
+import android.view.ViewGroup;
+
+/**
+ * A controller responsible for attaching and showing an optional low-light clock while dozing.
+ */
+public interface LowLightClockController {
+    /**
+     * Returns {@code true} if the low-light clock is enabled.
+     */
+    boolean isLowLightClockEnabled();
+
+    /**
+     * Attach the low light-clock to the given parent {@link ViewGroup}.
+     * @param parent The parent {@link ViewGroup} to which the low-light clock view should be
+     *               attached.
+     */
+    void attachLowLightClockView(ViewGroup parent);
+
+    /**
+     * Show or hide the low-light clock.
+     * @param show Whether to show the low-light clock.
+     * @return {@code true} if the low-light clock was shown.
+     */
+    boolean showLowLightClock(boolean show);
+
+    /**
+     * An opportunity to perform burn-in prevention.
+     */
+    void dozeTimeTick();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ecaa142..83ad027 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -540,14 +540,14 @@
     }
 
     private fun getBackgroundColor(): Int {
-        return context.getColor(android.R.color.system_accent2_50)
+        return context.getColor(R.color.material_dynamic_secondary95)
     }
 
     private fun getForegroundColor(): Int {
         return if (mediaFlags.useMediaSessionLayout()) {
-            context.getColor(android.R.color.system_neutral2_200)
+            context.getColor(R.color.material_dynamic_neutral_variant80)
         } else {
-            context.getColor(android.R.color.system_accent2_900)
+            context.getColor(R.color.material_dynamic_secondary10)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index ce7a697..831a606 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -476,7 +476,7 @@
         appIconView.clearColorFilter();
         if (data.getAppIcon() != null && !data.getResumption()) {
             appIconView.setImageIcon(data.getAppIcon());
-            int color = mContext.getColor(android.R.color.system_accent2_900);
+            int color = mContext.getColor(R.color.material_dynamic_secondary10);
             appIconView.setColorFilter(color);
         } else {
             // Resume players use launcher icon
@@ -590,7 +590,7 @@
             } else {
                 appIconView.setImageResource(R.drawable.ic_music_note);
             }
-            int color = mContext.getColor(android.R.color.system_accent2_900);
+            int color = mContext.getColor(R.color.material_dynamic_secondary10);
             appIconView.setColorFilter(color);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index e1ff110..7b85050 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -150,7 +150,7 @@
 
     private val themeText = com.android.settingslib.Utils.getColorAttr(context,
             com.android.internal.R.attr.textColorPrimary).defaultColor
-    private val bgColor = context.getColor(android.R.color.system_accent2_50)
+    private val bgColor = context.getColor(R.color.material_dynamic_secondary95)
 
     // Internal listeners are part of the internal pipeline. External listeners (those registered
     // with [MediaDeviceManager.addListener]) receive events after they have propagated through
@@ -531,7 +531,8 @@
         foregroundExecutor.execute {
             onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName,
                     null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
-                    null, packageName, token, appIntent, device = null, active = false,
+                    MediaButton(playOrPause = mediaAction), packageName, token, appIntent,
+                    device = null, active = false,
                     resumeAction = resumeAction, resumption = true, notificationKey = packageName,
                     hasCheckedForResume = true, lastActive = lastActive))
         }
@@ -951,6 +952,7 @@
             // Move to resume key (aka package name) if that key doesn't already exist.
             val resumeAction = getResumeMediaAction(removed.resumeAction!!)
             val updated = removed.copy(token = null, actions = listOf(resumeAction),
+                    semanticActions = MediaButton(playOrPause = resumeAction),
                     actionsToShowInCompact = listOf(0), active = false, resumption = true,
                     isPlaying = false, isClearable = true)
             val pkg = removed.packageName
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 2dff947..c3b4354 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,12 +16,7 @@
 
 package com.android.systemui.media.dagger;
 
-import android.content.Context;
-import android.os.Handler;
-import android.view.WindowManager;
-
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaFlags;
 import com.android.systemui.media.MediaHierarchyManager;
@@ -34,11 +29,8 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
 
 import java.util.Optional;
-import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
@@ -101,13 +93,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager) {
+            Lazy<MediaTttChipControllerSender> controllerSenderLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
+        return Optional.of(controllerSenderLazy.get());
     }
 
     /** */
@@ -115,16 +105,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager,
-            @Main Handler mainHandler) {
+            Lazy<MediaTttChipControllerReceiver> controllerReceiverLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttChipControllerReceiver(
-                        commandQueue, context, windowManager, mainHandler));
+        return Optional.of(controllerReceiverLazy.get());
     }
 
     /** */
@@ -132,14 +117,11 @@
     @SysUISingleton
     static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
             MediaTttFlags mediaTttFlags,
-            CommandRegistry commandRegistry,
-            Context context,
-            @Main Executor mainExecutor) {
+            Lazy<MediaTttCommandLineHelper> helperLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
+        return Optional.of(helperLazy.get());
     }
 
     /** */
@@ -147,13 +129,12 @@
     @SysUISingleton
     static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
             MediaFlags mediaFlags,
-            CommandRegistry commandRegistry,
-            Context context
+            Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
     ) {
         if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
+        return Optional.of(muteAwaitConnectionCliLazy.get());
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 5b29719..3cd3905 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -103,6 +103,7 @@
             }
             mCheckBox.setVisibility(View.GONE);
             mStatusIcon.setVisibility(View.GONE);
+            mContainerLayout.setOnClickListener(null);
             mTitleText.setTextColor(mController.getColorInactiveItem());
             mSeekBar.getProgressDrawable().setColorFilter(
                     new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
@@ -151,6 +152,7 @@
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(true);
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
@@ -169,6 +171,7 @@
                     initSeekbar(device);
                     mCurrentActivePosition = position;
                 } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                    mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(false);
                     mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
@@ -204,7 +207,7 @@
                 d.setColorFilter(new PorterDuffColorFilter(
                         Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
                 mTitleIcon.setImageDrawable(d);
-                mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+                mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
             }
         }
 
@@ -241,11 +244,5 @@
                 notifyDataSetChanged();
             }
         }
-
-        private void onItemClick(int customizedItem) {
-            if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
-                mController.launchBluetoothPairing();
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 85d1795..1f11d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -177,6 +177,9 @@
                                     .mutate() : mContext.getDrawable(
                             R.drawable.media_output_item_background)
                             .mutate();
+            backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
+                    mController.getColorItemBackground(),
+                    PorterDuff.Mode.SRC_IN));
             mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
@@ -212,8 +215,13 @@
             mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
             mSeekBar.setAlpha(1);
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
-            mItemLayout.setBackground(mContext.getDrawable(R.drawable.media_output_item_background)
-                    .mutate());
+            final Drawable backgroundDrawable = mContext.getDrawable(
+                            R.drawable.media_output_item_background)
+                    .mutate();
+            backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
+                    mController.getColorItemBackground(),
+                    PorterDuff.Mode.SRC_IN));
+            mItemLayout.setBackground(backgroundDrawable);
             mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
             mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
             mTwoLineTitleText.setTranslationY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index e55b25f..04a324b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -21,10 +21,15 @@
 
 import android.app.WallpaperColors;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -51,7 +56,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Base dialog for media output UI
@@ -76,8 +80,10 @@
     private ImageView mAppResourceIcon;
     private RecyclerView mDevicesRecyclerView;
     private LinearLayout mDeviceListLayout;
+    private LinearLayout mCastAppLayout;
     private Button mDoneButton;
     private Button mStopButton;
+    private Button mAppButton;
     private int mListMaxHeight;
     private WallpaperColors mWallpaperColors;
 
@@ -92,9 +98,8 @@
         }
     };
 
-    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController,
-            SystemUIDialogManager dialogManager) {
-        super(context, R.style.Theme_SystemUI_Dialog_Media, dialogManager);
+    public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
+        super(context, R.style.Theme_SystemUI_Dialog_Media);
 
         // Save the context that is wrapped with our theme.
         mContext = getContext();
@@ -129,7 +134,9 @@
         mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
         mDoneButton = mDialogView.requireViewById(R.id.done);
         mStopButton = mDialogView.requireViewById(R.id.stop);
+        mAppButton = mDialogView.requireViewById(R.id.launch_app_button);
         mAppResourceIcon = mDialogView.requireViewById(R.id.app_source_icon);
+        mCastAppLayout = mDialogView.requireViewById(R.id.cast_app_section);
 
         mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
                 mDeviceListLayoutListener);
@@ -144,6 +151,13 @@
             mMediaOutputController.releaseSession();
             dismiss();
         });
+        mAppButton.setOnClickListener(v -> {
+            mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+            if (mMediaOutputController.getAppLaunchIntent() != null) {
+                mContext.startActivity(mMediaOutputController.getAppLaunchIntent());
+            }
+            dismiss();
+        });
     }
 
     @Override
@@ -169,8 +183,16 @@
         final IconCompat iconCompat = getHeaderIcon();
         final Drawable appSourceDrawable = getAppSourceIcon();
         boolean colorSetUpdated = false;
+        mCastAppLayout.setVisibility(
+                mMediaOutputController.shouldShowLaunchSection()
+                        ? View.VISIBLE : View.GONE);
         if (appSourceDrawable != null) {
             mAppResourceIcon.setImageDrawable(appSourceDrawable);
+            mAppButton.setCompoundDrawablesWithIntrinsicBounds(resizeDrawable(appSourceDrawable,
+                            mContext.getResources().getDimensionPixelSize(
+                                    R.dimen.media_output_dialog_app_tier_icon_size
+                            )),
+                    null, null, null);
         } else {
             mAppResourceIcon.setVisibility(View.GONE);
         }
@@ -205,6 +227,7 @@
                     R.dimen.media_output_dialog_header_icon_padding);
             mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
         }
+        mAppButton.setText(mMediaOutputController.getAppSourceName());
         // Update title and subtitle
         mHeaderTitle.setText(getHeaderText());
         final CharSequence subTitle = getHeaderSubtitle();
@@ -229,6 +252,22 @@
         mStopButton.setVisibility(getStopButtonVisibility());
     }
 
+    private Drawable resizeDrawable(Drawable drawable, int size) {
+        if (drawable == null) {
+            return null;
+        }
+        int width = drawable.getIntrinsicWidth();
+        int height = drawable.getIntrinsicHeight();
+        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
+                : Bitmap.Config.RGB_565;
+        Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, width, height);
+        drawable.draw(canvas);
+        return new BitmapDrawable(mContext.getResources(),
+                Bitmap.createScaledBitmap(bitmap, size, size, false));
+    }
+
     abstract Drawable getAppSourceIcon();
 
     abstract int getHeaderIconRes();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 1c6b7dc..e929b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -22,6 +22,7 @@
 import android.app.WallpaperColors;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
@@ -45,6 +46,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.mediarouter.media.MediaRouter;
+import androidx.mediarouter.media.MediaRouterParams;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -56,13 +59,13 @@
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -87,7 +90,6 @@
     private final ShadeController mShadeController;
     private final ActivityStarter mActivityStarter;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
-    private final SystemUIDialogManager mDialogManager;
     private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
     private final boolean mAboveStatusbar;
     private final boolean mVolumeAdjustmentForRemoteGroupSessions;
@@ -108,13 +110,14 @@
     private int mColorInactiveItem;
     private int mColorSeekbarProgress;
     private int mColorButtonBackground;
+    private int mColorItemBackground;
 
     @Inject
     public MediaOutputController(@NonNull Context context, String packageName,
             boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
             lbm, ShadeController shadeController, ActivityStarter starter,
             CommonNotifCollection notifCollection, UiEventLogger uiEventLogger,
-            DialogLaunchAnimator dialogLaunchAnimator, SystemUIDialogManager dialogManager) {
+            DialogLaunchAnimator dialogLaunchAnimator) {
         mContext = context;
         mPackageName = packageName;
         mMediaSessionManager = mediaSessionManager;
@@ -130,7 +133,6 @@
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
-        mDialogManager = dialogManager;
         mColorActiveItem = Utils.getColorStateListDefaultColor(mContext,
                 R.color.media_dialog_active_item_main_content);
         mColorInactiveItem = Utils.getColorStateListDefaultColor(mContext,
@@ -139,6 +141,8 @@
                 android.R.color.system_accent1_200);
         mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext,
                 R.color.media_dialog_item_background);
+        mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
+                android.R.color.system_accent2_50);
     }
 
     void start(@NonNull Callback cb) {
@@ -171,6 +175,12 @@
         mLocalMediaManager.startScan();
     }
 
+    boolean shouldShowLaunchSection() {
+        MediaRouterParams routerParams = MediaRouter.getInstance(mContext).getRouterParams();
+        Log.d(TAG, "try to get routerParams: " + routerParams);
+        return routerParams != null && !routerParams.isMediaTransferReceiverEnabled();
+    }
+
     void stop() {
         if (mMediaController != null) {
             mMediaController.unregisterCallback(mCb);
@@ -220,6 +230,32 @@
         }
     }
 
+    String getAppSourceName() {
+        if (mPackageName.isEmpty()) {
+            return null;
+        }
+        final PackageManager packageManager = mContext.getPackageManager();
+        ApplicationInfo applicationInfo;
+        try {
+            applicationInfo = packageManager.getApplicationInfo(mPackageName,
+                    PackageManager.ApplicationInfoFlags.of(0));
+        } catch (PackageManager.NameNotFoundException e) {
+            applicationInfo = null;
+        }
+        final String applicationName =
+                (String) (applicationInfo != null ? packageManager.getApplicationLabel(
+                        applicationInfo)
+                        : mContext.getString(R.string.media_output_dialog_unknown_launch_app_name));
+        return applicationName;
+    }
+
+    Intent getAppLaunchIntent() {
+        if (mPackageName.isEmpty()) {
+            return null;
+        }
+        return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
+    }
+
     CharSequence getHeaderTitle() {
         if (mMediaController != null) {
             final MediaMetadata metadata = mMediaController.getMetadata();
@@ -315,14 +351,16 @@
                 isDarkTheme);
         if (isDarkTheme) {
             mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
-            mColorInactiveItem = mCurrentColorScheme.getAccent1().get(2);
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
+            mColorInactiveItem = mCurrentColorScheme.getNeutral1().get(10);
+            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(2);
             mColorButtonBackground = mCurrentColorScheme.getAccent1().get(2);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         } else {
             mColorActiveItem = mCurrentColorScheme.getNeutral1().get(10);
             mColorInactiveItem = mCurrentColorScheme.getAccent1().get(7);
             mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(3);
-            mColorButtonBackground = mCurrentColorScheme.getAccent2().get(1);
+            mColorButtonBackground = mCurrentColorScheme.getAccent1().get(3);
+            mColorItemBackground = mCurrentColorScheme.getAccent2().get(0);
         }
     }
 
@@ -342,6 +380,10 @@
         return mColorButtonBackground;
     }
 
+    public int getColorItemBackground() {
+        return mColorItemBackground;
+    }
+
     private void buildMediaDevices(List<MediaDevice> devices) {
         // For the first time building list, to make sure the top device is the connected device.
         if (mMediaDevices.isEmpty()) {
@@ -533,12 +575,14 @@
         return false;
     }
 
-    void launchBluetoothPairing() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
+    void launchBluetoothPairing(View view) {
+        ActivityLaunchAnimator.Controller controller =
+                mDialogLaunchAnimator.createActivityLaunchController(view);
 
-        mCallback.dismissDialog();
+        if (controller == null) {
+            mCallback.dismissDialog();
+        }
+
         Intent launchIntent =
                 new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -553,20 +597,19 @@
             deepLinkIntent.putExtra(
                     Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
                     PAGE_CONNECTED_DEVICES_KEY);
-            mActivityStarter.startActivity(deepLinkIntent, true);
+            mActivityStarter.startActivity(deepLinkIntent, true, controller);
             return;
         }
-        mActivityStarter.startActivity(launchIntent, true);
+        mActivityStarter.startActivity(launchIntent, true, controller);
     }
 
     void launchMediaOutputGroupDialog(View mediaOutputDialog) {
         // We show the output group dialog from the output dialog.
         MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
                 mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
-                mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator,
-                mDialogManager);
+                mActivityStarter, mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
         MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
-                controller, mDialogManager);
+                controller);
         mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 4e9da55..7696a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -29,7 +29,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output transferring.
@@ -39,9 +38,8 @@
     final UiEventLogger mUiEventLogger;
 
     MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController, UiEventLogger uiEventLogger,
-            SystemUIDialogManager dialogManager) {
-        super(context, mediaOutputController, dialogManager);
+            mediaOutputController, UiEventLogger uiEventLogger) {
+        super(context, mediaOutputController);
         mUiEventLogger = uiEventLogger;
         mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index e1e7fa3..9e252ea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.phone.ShadeController
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import javax.inject.Inject
 
 /**
@@ -39,8 +38,7 @@
     private val starter: ActivityStarter,
     private val notifCollection: CommonNotifCollection,
     private val uiEventLogger: UiEventLogger,
-    private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val dialogManager: SystemUIDialogManager
+    private val dialogLaunchAnimator: DialogLaunchAnimator
 ) {
     companion object {
         var mediaOutputDialog: MediaOutputDialog? = null
@@ -53,9 +51,8 @@
 
         val controller = MediaOutputController(context, packageName, aboveStatusBar,
             mediaSessionManager, lbm, shadeController, starter, notifCollection,
-            uiEventLogger, dialogLaunchAnimator, dialogManager)
-        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger,
-                dialogManager)
+            uiEventLogger, dialogLaunchAnimator)
+        val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
         mediaOutputDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 9f752b9..f1c6601 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -25,7 +25,6 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 /**
  * Dialog for media output group.
@@ -34,8 +33,8 @@
 public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
 
     MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
-            mediaOutputController, SystemUIDialogManager dialogManager) {
-        super(context, mediaOutputController, dialogManager);
+            mediaOutputController) {
+        super(context, mediaOutputController);
         mMediaOutputController.resetGroupMediaDevices();
         mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
         if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 4993105..6ec2b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -25,8 +25,12 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.DelayableExecutor
 
 /**
  * A superclass controller that provides common functionality for showing chips on the sender device
@@ -38,6 +42,8 @@
 abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
     internal val context: Context,
     private val windowManager: WindowManager,
+    @Main private val mainExecutor: DelayableExecutor,
+    private val tapGestureDetector: TapGestureDetector,
     @LayoutRes private val chipLayoutRes: Int
 ) {
     /** The window layout parameters we'll use when attaching the view to a window. */
@@ -56,6 +62,9 @@
     /** The chip view currently being displayed. Null if the chip is not being displayed. */
     var chipView: ViewGroup? = null
 
+    /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
+    var cancelChipViewTimeout: Runnable? = null
+
     /**
      * Displays the chip with the current state.
      *
@@ -75,10 +84,14 @@
 
         // Add view if necessary
         if (oldChipView == null) {
+            tapGestureDetector.addOnGestureDetectedCallback(TAG, this::removeChip)
             windowManager.addView(chipView, windowLayoutParams)
         }
-    }
 
+        // Cancel and re-set the chip timeout each time we get a new state.
+        cancelChipViewTimeout?.run()
+        cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
+    }
 
     /** Hides the chip. */
     fun removeChip() {
@@ -86,6 +99,7 @@
         //  TransferTriggered state: Once the user has initiated the transfer, they should be able
         //  to move away from the receiver device but still see the status of the transfer.
         if (chipView == null) { return }
+        tapGestureDetector.removeOnGestureDetectedCallback(TAG)
         windowManager.removeView(chipView)
         chipView = null
     }
@@ -118,3 +132,6 @@
 // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
 // UpdateMediaTapToTransferReceiverDisplayTest
 private const val WINDOW_TITLE = "Media Transfer Chip View"
+private val TAG = MediaTttChipControllerCommon::class.simpleName!!
+@VisibleForTesting
+const val TIMEOUT_MILLIS = 3000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 18623c0..b6f1aea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -18,7 +18,6 @@
 
 import android.app.StatusBarManager
 import android.content.Context
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.media.MediaRoute2Info
 import android.os.Handler
@@ -27,10 +26,11 @@
 import android.view.WindowManager
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -43,9 +43,11 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    mainExecutor: DelayableExecutor,
+    tapGestureDetector: TapGestureDetector,
     @Main private val mainHandler: Handler,
 ) : MediaTttChipControllerCommon<ChipStateReceiver>(
-    context, windowManager, R.layout.media_ttt_chip_receiver
+    context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip_receiver
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index da767ea..fef17fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -27,8 +27,11 @@
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -40,8 +43,10 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    @Main mainExecutor: DelayableExecutor,
+    tapGestureDetector: TapGestureDetector,
 ) : MediaTttChipControllerCommon<ChipStateSender>(
-    context, windowManager, R.layout.media_ttt_chip
+    context, windowManager, mainExecutor,  tapGestureDetector, R.layout.media_ttt_chip
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferSenderDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d16c019..8b39e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -542,7 +542,7 @@
         return mNavigationBarView;
     }
 
-    public View createView(Bundle savedState) {
+    public View createView(Bundle savedState, boolean initialVisibility) {
         mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
                 R.layout.navigation_bar_window, null);
         View barView = LayoutInflater.from(mFrame.getContext()).inflate(
@@ -550,6 +550,8 @@
         barView.addOnAttachStateChangeListener(this);
         mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
 
+        mNavigationBarView.setVisibility(initialVisibility ? View.VISIBLE : View.INVISIBLE);
+
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
         mWindowManager.addView(mFrame,
                 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index aa1117c..a049736 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
@@ -85,6 +86,7 @@
     private final NavigationBar.Factory mNavigationBarFactory;
     private final DisplayManager mDisplayManager;
     private final TaskbarDelegate mTaskbarDelegate;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private int mNavMode;
     @VisibleForTesting boolean mIsTablet;
 
@@ -108,6 +110,7 @@
             NavBarHelper navBarHelper,
             TaskbarDelegate taskbarDelegate,
             NavigationBar.Factory navigationBarFactory,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
@@ -122,6 +125,7 @@
         mConfigChanges.applyNewConfig(mContext.getResources());
         mNavMode = navigationModeController.addListener(this);
         mTaskbarDelegate = taskbarDelegate;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
                 dumpManager, autoHideController, lightBarController, pipOptional,
@@ -323,7 +327,8 @@
 
         mNavigationBars.put(displayId, navBar);
 
-        View navigationBarView = navBar.createView(savedState);
+        boolean navBarVisible = mStatusBarKeyguardViewManager.isNavBarVisible();
+        View navigationBarView = navBar.createView(savedState, navBarVisible);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 593b278c..80a7a4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -125,6 +125,7 @@
     int mDisabledFlags = 0;
     int mNavigationIconHints = 0;
     private int mNavBarMode;
+    private boolean mImeDrawsImeNavBar;
 
     private final Region mTmpRegion = new Region();
     private final int[] mTmpPosition = new int[2];
@@ -273,11 +274,21 @@
             };
 
     private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
-        // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
-        // gestural mode, the entire nav bar should be touchable.
+        // When the nav bar is in 2-button or 3-button mode, or when the back button is force-shown
+        // while in gesture nav in SUW, the entire nav bar should be touchable.
         if (!mEdgeBackGestureHandler.isHandlingGestures()) {
-            info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
-            return;
+            // We're in 2/3 button mode OR back button force-shown in SUW
+            if (!mImeVisible) {
+                // IME not showing, take all touches
+                info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                return;
+            }
+  
+            if (!isImeRenderingNavButtons()) {
+                // IME showing but not drawing any buttons, take all touches
+                info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                return;     
+            }
         }
 
         // When in gestural and the IME is showing, don't use the nearest region since it will take
@@ -324,6 +335,7 @@
         mIsVertical = false;
         mLongClickableAccessibilityButton = false;
         mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
+        mImeDrawsImeNavBar = Dependency.get(NavigationModeController.class).getImeDrawsImeNavBar();
 
         mSysUiFlagContainer = Dependency.get(SysUiState.class);
         // Set up the context group of buttons
@@ -773,14 +785,10 @@
 
         updateRecentsIcon();
 
-        boolean isImeRenderingNavButtons = isGesturalMode(mNavBarMode)
-                && mImeCanRenderGesturalNavButtons
-                && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
-
         // Update IME button visibility, a11y and rotate button always overrides the appearance
         boolean disableImeSwitcher =
                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
-                || isImeRenderingNavButtons;
+                || isImeRenderingNavButtons();
         mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
 
         mBarTransitions.reapplyDarkIntensity();
@@ -797,7 +805,7 @@
 
         boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
-                || isImeRenderingNavButtons;
+                || isImeRenderingNavButtons();
 
         // When screen pinning, don't hide back and home when connected service or back and
         // recents buttons when disconnected from launcher service in screen pinning mode,
@@ -830,6 +838,15 @@
         notifyActiveTouchRegions();
     }
 
+    /**
+     * Returns whether the IME is currently visible and drawing the nav buttons.
+     */
+    private boolean isImeRenderingNavButtons() {
+        return mImeDrawsImeNavBar
+                && mImeCanRenderGesturalNavButtons
+                && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+    }
+
     @VisibleForTesting
     boolean isRecentsButtonDisabled() {
         return mUseCarModeUi || !isOverviewEnabled()
@@ -966,6 +983,7 @@
     @Override
     public void onNavigationModeChanged(int mode) {
         mNavBarMode = mode;
+        mImeDrawsImeNavBar = Dependency.get(NavigationModeController.class).getImeDrawsImeNavBar();
         mBarTransitions.onNavigationModeChanged(mNavBarMode);
         mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
         updateRotationButton();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index 73a0c54..6920ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -156,6 +156,11 @@
         mListeners.remove(listener);
     }
 
+    public boolean getImeDrawsImeNavBar() {
+        return mCurrentUserContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_imeDrawsImeNavBar);
+    }
+
     private int getCurrentInteractionMode(Context context) {
         int mode = context.getResources().getInteger(
                 com.android.internal.R.integer.config_navBarInteractionMode);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9199911..9ea2763 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -263,15 +263,6 @@
                     // Notify FalsingManager that an intentional gesture has occurred.
                     // TODO(b/186519446): use a different method than isFalseTouch
                     mFalsingManager.isFalseTouch(BACK_GESTURE);
-                    // Only inject back keycodes when ahead-of-time back dispatching is disabled.
-                    if (mBackAnimation == null) {
-                        boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
-                        boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
-                        if (DEBUG_MISSING_GESTURE) {
-                            Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
-                                    + sendDown + ", up=" + sendUp);
-                        }
-                    }
 
                     mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
                             (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 23482677..fe4cb71 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.privacy
 
 import android.content.Context
+import android.content.Intent
 import android.graphics.drawable.LayerDrawable
 import android.os.Bundle
 import android.text.TextUtils
@@ -40,12 +41,12 @@
  * @param context A context to create the dialog
  * @param list list of elements to show in the dialog. The elements will show in the same order they
  *             appear in the list
- * @param activityStarter a callback to start an activity for a given package name and user id
+ * @param activityStarter a callback to start an activity for a given package name, user id, attributionTag and intent
  */
 class PrivacyDialog(
     context: Context,
     private val list: List<PrivacyElement>,
-    activityStarter: (String, Int) -> Unit
+    activityStarter: (String, Int, CharSequence?, Intent?) -> Unit
 ) : SystemUIDialog(context, R.style.PrivacyDialog) {
 
     private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
@@ -118,13 +119,7 @@
             app
         }
         val firstLine = context.getString(stringId, appName)
-        val finalText = element.attribution?.let {
-            TextUtils.concat(
-                    firstLine,
-                    " ",
-                    context.getString(R.string.ongoing_privacy_dialog_attribution_text, it)
-            )
-        } ?: firstLine
+        val finalText = getFinalText(firstLine, element.attributionLabel, element.proxyLabel)
         newView.requireViewById<TextView>(R.id.text).text = finalText
         if (element.phoneCall) {
             newView.requireViewById<View>(R.id.chevron).visibility = View.GONE
@@ -138,6 +133,25 @@
         return newView
     }
 
+    private fun getFinalText(
+        firstLine: CharSequence,
+        attributionLabel: CharSequence?,
+        proxyLabel: CharSequence?
+    ): CharSequence {
+        var dialogText: CharSequence? = null
+        if (attributionLabel != null && proxyLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_proxy_label,
+                attributionLabel, proxyLabel)
+        } else if (attributionLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_label,
+                attributionLabel)
+        } else if (proxyLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_text,
+                proxyLabel)
+        }
+        return if (dialogText != null) TextUtils.concat(firstLine, " ", dialogText) else firstLine
+    }
+
     private fun getStringIdForState(active: Boolean): Int {
         return if (active) {
             R.string.ongoing_privacy_dialog_using_op
@@ -157,7 +171,8 @@
     private val clickListener = View.OnClickListener { v ->
         v.tag?.let {
             val element = it as PrivacyElement
-            activityStarter(element.packageName, element.userId)
+            activityStarter(element.packageName, element.userId,
+                element.attributionTag, element.navigationIntent)
         }
     }
 
@@ -167,11 +182,15 @@
         val packageName: String,
         val userId: Int,
         val applicationName: CharSequence,
-        val attribution: CharSequence?,
+        val attributionTag: CharSequence?,
+        val attributionLabel: CharSequence?,
+        val proxyLabel: CharSequence?,
         val lastActiveTimestamp: Long,
         val active: Boolean,
         val enterprise: Boolean,
-        val phoneCall: Boolean
+        val phoneCall: Boolean,
+        val permGroupName: CharSequence,
+        val navigationIntent: Intent?
     ) {
         private val builder = StringBuilder("PrivacyElement(")
 
@@ -180,8 +199,14 @@
             builder.append(", packageName=$packageName")
             builder.append(", userId=$userId")
             builder.append(", appName=$applicationName")
-            if (attribution != null) {
-                builder.append(", attribution=$attribution")
+            if (attributionTag != null) {
+                builder.append(", attributionTag=$attributionTag")
+            }
+            if (attributionLabel != null) {
+                builder.append(", attributionLabel=$attributionLabel")
+            }
+            if (proxyLabel != null) {
+                builder.append(", proxyLabel=$proxyLabel")
             }
             builder.append(", lastActive=$lastActiveTimestamp")
             if (active) {
@@ -193,7 +218,10 @@
             if (phoneCall) {
                 builder.append(", phoneCall")
             }
-            builder.append(")")
+            builder.append(", permGroupName=$permGroupName)")
+            if (navigationIntent != null) {
+                builder.append(", navigationIntent=$navigationIntent")
+            }
         }
 
         override fun toString(): String = builder.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index d0aa710..6107123 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -19,11 +19,12 @@
 import android.Manifest
 import android.app.ActivityManager
 import android.app.Dialog
+import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
 import android.util.Log
 import androidx.annotation.MainThread
@@ -45,11 +46,12 @@
     override fun makeDialog(
         context: Context,
         list: List<PrivacyDialog.PrivacyElement>,
-        starter: (String, Int) -> Unit
+        starter: (String, Int, CharSequence?, Intent?) -> Unit
     ): PrivacyDialog {
         return PrivacyDialog(context, list, starter)
     }
 }
+
 /**
  * Controller for [PrivacyDialog].
  *
@@ -115,12 +117,19 @@
     }
 
     @MainThread
-    private fun startActivity(packageName: String, userId: Int) {
-        val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
-        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
-        intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+    private fun startActivity(
+        packageName: String,
+        userId: Int,
+        attributionTag: CharSequence?,
+        navigationIntent: Intent?
+    ) {
+        val intent = if (navigationIntent == null) {
+            getDefaultManageAppPermissionsIntent(packageName, userId)
+        } else {
+            navigationIntent
+        }
         uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
-                userId, packageName)
+            userId, packageName)
         privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
         if (!keyguardStateController.isUnlocked) {
             // If we are locked, hide the dialog so the user can unlock
@@ -137,7 +146,39 @@
     }
 
     @WorkerThread
-    private fun permGroupUsage(): List<PermGroupUsage> {
+    private fun getManagePermissionIntent(
+        packageName: String,
+        userId: Int,
+        permGroupName: CharSequence,
+        attributionTag: CharSequence?
+    ): Intent
+    {
+        lateinit var intent: Intent
+        if (attributionTag != null) {
+            intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
+            intent.setPackage(packageName)
+            intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName.toString())
+            intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(attributionTag.toString()))
+            intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
+            val resolveInfo = packageManager.resolveActivity(
+                intent, PackageManager.ResolveInfoFlags.of(0))
+                ?: return getDefaultManageAppPermissionsIntent(packageName, userId)
+            intent.component = ComponentName(packageName, resolveInfo.activityInfo.name)
+            return intent
+        } else {
+            return getDefaultManageAppPermissionsIntent(packageName, userId)
+        }
+    }
+
+    fun getDefaultManageAppPermissionsIntent(packageName: String, userId: Int): Intent {
+        val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+        intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+        return intent
+    }
+
+    @WorkerThread
+    private fun permGroupUsage(): List<PermissionGroupUsage> {
         return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
     }
 
@@ -160,7 +201,7 @@
             val userInfos = userTracker.userProfiles
             privacyLogger.logUnfilteredPermGroupUsage(usage)
             val items = usage.mapNotNull {
-                val type = filterType(permGroupToPrivacyType(it.permGroupName))
+                val type = filterType(permGroupToPrivacyType(it.permissionGroupName))
                 val userInfo = userInfos.firstOrNull { ui -> ui.id == UserHandle.getUserId(it.uid) }
                 if (userInfo != null || it.isPhoneCall) {
                     type?.let { t ->
@@ -170,17 +211,24 @@
                         } else {
                             getLabelForPackage(it.packageName, it.uid)
                         }
+                        val userId = UserHandle.getUserId(it.uid)
                         PrivacyDialog.PrivacyElement(
                                 t,
                                 it.packageName,
-                                UserHandle.getUserId(it.uid),
+                                userId,
                                 appName,
-                                it.attribution,
-                                it.lastAccess,
+                                it.attributionTag,
+                                it.attributionLabel,
+                                it.proxyLabel,
+                                it.lastAccessTimeMillis,
                                 it.isActive,
                                 // If there's no user info, we're in a phoneCall in secondary user
                                 userInfo?.isManagedProfile ?: false,
-                                it.isPhoneCall
+                                it.isPhoneCall,
+                                it.permissionGroupName,
+                                getManagePermissionIntent(it.packageName, userId,
+                                    it.permissionGroupName,
+                                it.attributionTag)
                         )
                     }
                 } else {
@@ -215,8 +263,8 @@
     private fun getLabelForPackage(packageName: String, uid: Int): CharSequence {
         return try {
             packageManager
-                    .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
-                    .loadLabel(packageManager)
+                .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
+                .loadLabel(packageManager)
         } catch (_: PackageManager.NameNotFoundException) {
             Log.w(TAG, "Label not found for: $packageName")
             packageName
@@ -235,7 +283,7 @@
     private fun filterType(type: PrivacyType?): PrivacyType? {
         return type?.let {
             if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
-                    privacyItemController.micCameraAvailable) {
+                privacyItemController.micCameraAvailable) {
                 it
             } else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
                 it
@@ -278,7 +326,7 @@
         fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String, Int) -> Unit
+            starter: (String, Int, CharSequence?, Intent?) -> Unit
         ): PrivacyDialog
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 7c82a82..1a268b5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.privacy.logging
 
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogLevel
 import com.android.systemui.log.LogMessage
@@ -100,7 +100,7 @@
         })
     }
 
-    fun logUnfilteredPermGroupUsage(contents: List<PermGroupUsage>) {
+    fun logUnfilteredPermGroupUsage(contents: List<PermissionGroupUsage>) {
         log(LogLevel.DEBUG, {
             str1 = contents.toString()
         }, {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
index 6a6f572..e473dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AlphaControlledSignalTileView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
@@ -73,7 +74,7 @@
         }
 
         @Override
-        protected void setDrawableTintList(ColorStateList tint) {
+        protected void setDrawableTintList(@Nullable ColorStateList tint) {
         }
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 3b305bb..8afb793 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -1,8 +1,16 @@
 package com.android.systemui.qs
 
+import android.content.Intent
+import android.permission.PermissionGroupUsage
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
 import android.view.View
+import androidx.annotation.WorkerThread
 import com.android.internal.R
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyChipEvent
 import com.android.systemui.privacy.PrivacyDialogController
@@ -10,7 +18,11 @@
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.DeviceConfigProxy
+import java.util.concurrent.Executor
 import javax.inject.Inject
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 
 interface ChipVisibilityListener {
     fun onChipVisibilityRefreshed(visible: Boolean)
@@ -32,18 +44,46 @@
     private val privacyChip: OngoingPrivacyChip,
     private val privacyDialogController: PrivacyDialogController,
     private val privacyLogger: PrivacyLogger,
-    private val iconContainer: StatusIconContainer
+    private val iconContainer: StatusIconContainer,
+    private val permissionManager: PermissionManager,
+    @Background private val backgroundExecutor: Executor,
+    @Main private val uiExecutor: Executor,
+    private val activityStarter: ActivityStarter,
+    private val appOpsController: AppOpsController,
+    private val deviceConfigProxy: DeviceConfigProxy
 ) {
 
+    companion object {
+        const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+    }
+
     var chipVisibilityListener: ChipVisibilityListener? = null
+
     private var listening = false
     private var micCameraIndicatorsEnabled = false
     private var locationIndicatorsEnabled = false
     private var privacyChipLogged = false
+    private var safetyCenterEnabled = false
     private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
     private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
     private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
 
+    private val devicePropertiesChangedListener =
+        object : DeviceConfig.OnPropertiesChangedListener {
+            override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+                safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false)
+            }
+        }
+
+    init {
+        safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            SAFETY_CENTER_ENABLED, false)
+        deviceConfigProxy.addOnPropertiesChangedListener(
+            DeviceConfig.NAMESPACE_PRIVACY,
+            uiExecutor,
+            devicePropertiesChangedListener)
+    }
+
     private val picCallback: PrivacyItemController.Callback =
             object : PrivacyItemController.Callback {
         override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
@@ -77,7 +117,11 @@
         privacyChip.setOnClickListener {
             // If the privacy chip is visible, it means there were some indicators
             uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
-            privacyDialogController.showDialog(privacyChip.context)
+            if (safetyCenterEnabled) {
+                showSafetyHub()
+            } else {
+                privacyDialogController.showDialog(privacyChip.context)
+            }
         }
         setChipVisibility(privacyChip.visibility == View.VISIBLE)
         micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
@@ -87,6 +131,26 @@
         updatePrivacyIconSlots()
     }
 
+    private fun showSafetyHub() {
+        backgroundExecutor.execute {
+            val usage = ArrayList(permGroupUsage())
+            privacyLogger.logUnfilteredPermGroupUsage(usage)
+            val startSafetyHub = Intent(Intent.ACTION_VIEW_SAFETY_HUB)
+            startSafetyHub.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
+                usage)
+            startSafetyHub.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            uiExecutor.execute {
+                activityStarter.startActivity(startSafetyHub, true,
+                    ActivityLaunchAnimator.Controller.fromView(privacyChip))
+            }
+        }
+    }
+
+    @WorkerThread
+    private fun permGroupUsage(): List<PermissionGroupUsage> {
+        return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
+    }
+
     fun onParentInvisible() {
         chipVisibilityListener = null
         privacyChip.setOnClickListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
index 67cfc59..26399d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
@@ -27,6 +27,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.R;
 
 import java.util.Objects;
@@ -48,6 +50,7 @@
     private final TextView mSecondLine;
     private final int mHorizontalPaddingPx;
 
+    @Nullable
     private String mText;
 
     public QSDualTileLabel(Context context) {
@@ -122,6 +125,7 @@
         rescheduleUpdateText();
     }
 
+    @Nullable
     public String getText() {
         return mText;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 17f85ee..3c7933f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -381,14 +381,17 @@
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+        boolean qsPanelVisible = !mQsDisabled && expandVisually;
+        boolean footerVisible = qsPanelVisible &&  (expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard);
         mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
         mQSFooterActionController.setVisible(footerVisible);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling));
-        mQSPanelController.setVisibility(
-                !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+        mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
+        if (DEBUG) {
+            Log.d(TAG, "Footer: " + footerVisible + ", QS Panel: " + qsPanelVisible);
+        }
     }
 
     private boolean isKeyguardState() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0c854df..5126fcb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -48,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /** View that represents the quick settings tile panel (when expanded/pulled down). **/
 public class QSPanel extends LinearLayout implements Tunable {
@@ -78,7 +79,7 @@
     protected boolean mExpanded;
     protected boolean mListening;
 
-    protected QSTileHost mHost;
+    @Nullable protected QSTileHost mHost;
     private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
             new ArrayList<>();
 
@@ -92,14 +93,18 @@
 
     @Nullable
     private ViewGroup mHeaderContainer;
+    @Nullable
     private PageIndicator mFooterPageIndicator;
     private int mContentMarginStart;
     private int mContentMarginEnd;
     private boolean mUsingHorizontalLayout;
 
+    @Nullable
     private LinearLayout mHorizontalLinearLayout;
+    @Nullable
     protected LinearLayout mHorizontalContentContainer;
 
+    @Nullable
     protected QSTileLayout mTileLayout;
     private float mSquishinessFraction = 1f;
     private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
@@ -284,7 +289,7 @@
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
             if (move) {
-                int top = mChildrenLayoutTop.get(child);
+                int top = Objects.requireNonNull(mChildrenLayoutTop.get(child));
                 child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
                         child.getRight(), top + tileHeightOffset + child.getHeight());
             }
@@ -337,6 +342,7 @@
         }
     }
 
+    @Nullable
     public QSTileHost getHost() {
         return mHost;
     }
@@ -501,7 +507,6 @@
         mListening = listening;
     }
 
-
     protected void drawTile(QSPanelControllerBase.TileRecord r, QSTile.State state) {
         r.tileView.onStateChanged(state);
     }
@@ -548,6 +553,7 @@
         return getMeasuredHeight();
     }
 
+    @Nullable
     QSTileLayout getTileLayout() {
         return mTileLayout;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 0bff722..3172aa9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -76,6 +76,7 @@
     private Consumer<Boolean> mMediaVisibilityChangedListener;
     private int mLastOrientation;
     private String mCachedSpecs = "";
+    @Nullable
     private QSTileRevealController mQsTileRevealController;
     private float mRevealExpansion;
 
@@ -185,6 +186,7 @@
         mDumpManager.unregisterDumpable(mView.getDumpableTag());
     }
 
+    @Nullable
     protected QSTileRevealController createTileRevealController() {
         return null;
     }
@@ -250,6 +252,7 @@
         return !mRecords.isEmpty();
     }
 
+    @Nullable
     QSTileView getTileView(QSTile tile) {
         for (QSPanelControllerBase.TileRecord r : mRecords) {
             if (r.tile == tile) {
@@ -411,6 +414,7 @@
         mUsingHorizontalLayoutChangedListener = listener;
     }
 
+    @Nullable
     public View getBrightnessView() {
         return mView.getBrightnessView();
     }
@@ -425,6 +429,7 @@
         public QSTile tile;
         public com.android.systemui.plugins.qs.QSTileView tileView;
         public boolean scanState;
+        @Nullable
         public QSTile.Callback callback;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 4b705ad..6908e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -20,6 +20,7 @@
 
 import android.annotation.MainThread;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -81,6 +82,7 @@
     private final CarrierConfigTracker mCarrierConfigTracker;
 
     private boolean mIsSingleCarrier;
+    @Nullable
     private OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
 
     private final SlotIndexResolver mSlotIndexResolver;
@@ -294,7 +296,8 @@
      * This will get notified when the number of carriers changes between 1 and "not one".
      * @param listener
      */
-    public void setOnSingleCarrierChangedListener(OnSingleCarrierChangedListener listener) {
+    public void setOnSingleCarrierChangedListener(
+            @Nullable OnSingleCarrierChangedListener listener) {
         mOnSingleCarrierChangedListener = listener;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 90cf92a..c1970b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -96,15 +96,20 @@
     private int mFocusIndex;
 
     private boolean mNeedsFocus;
+    @Nullable
     private List<String> mCurrentSpecs;
+    @Nullable
     private List<TileInfo> mOtherTiles;
+    @Nullable
     private List<TileInfo> mAllTiles;
 
+    @Nullable
     private Holder mCurrentDrag;
     private int mAccessibilityAction = ACTION_NONE;
     private int mAccessibilityFromIndex;
     private final UiEventLogger mUiEventLogger;
     private final AccessibilityDelegateCompat mAccessibilityDelegate;
+    @Nullable
     private RecyclerView mRecyclerView;
     private int mNumColumns;
 
@@ -240,6 +245,7 @@
         notifyDataSetChanged();
     }
 
+    @Nullable
     private TileInfo getAndRemoveOther(String s) {
         for (int i = 0; i < mOtherTiles.size(); i++) {
             if (mOtherTiles.get(i).spec.equals(s)) {
@@ -555,7 +561,7 @@
     }
 
     public class Holder extends ViewHolder {
-        private QSTileViewImpl mTileView;
+        @Nullable private QSTileViewImpl mTileView;
 
         public Holder(View itemView) {
             super(itemView);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 106a1b6..7fb9ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -17,6 +17,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -49,6 +50,7 @@
     private boolean mAnimationEnabled = true;
     private int mState = -1;
     private int mTint;
+    @Nullable
     private QSTile.Icon mLastIcon;
 
     public QSIconViewImpl(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index e8d27ec..5d6bbae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -36,6 +36,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -191,15 +192,22 @@
                     mContext,
                     ROUTE_TYPE_REMOTE_DISPLAY,
                     v -> {
-                        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-                        holder.mDialog.dismiss();
+                        ActivityLaunchAnimator.Controller controller =
+                                mDialogLaunchAnimator.createActivityLaunchController(v);
+
+                        if (controller == null) {
+                            holder.mDialog.dismiss();
+                        }
+
                         mActivityStarter
-                                .postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
-                    });
+                                .postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
+                                        controller);
+                    }, R.style.Theme_SystemUI_Dialog, false /* showProgressBarWhenEmpty */);
             holder.init(dialog);
             SystemUIDialog.setShowForAllUsers(dialog, true);
             SystemUIDialog.registerDismissListener(dialog);
             SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
+            SystemUIDialog.setDialogSize(dialog);
 
             mUiHandler.post(() -> {
                 if (view != null) {
@@ -316,5 +324,5 @@
         public void onKeyguardShowingChanged() {
             refreshState();
         }
-    };
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 4fe155c..8bad2de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -47,9 +47,6 @@
 public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
 
     private static final String TAG = "InternetAdapter";
-    private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
-    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
-    private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
 
     private final InternetDialogController mInternetDialogController;
     @Nullable
@@ -164,16 +161,15 @@
             if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
                 mWifiListLayout.setOnClickListener(
                         v -> mInternetDialogController.launchWifiNetworkDetailsSetting(
-                                wifiEntry.getKey()));
+                                wifiEntry.getKey(), v));
                 return;
             }
             mWifiListLayout.setOnClickListener(v -> {
                 if (wifiEntry.shouldEditBeforeConnect()) {
-                    final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+                    final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(),
+                            true /* connectForCaller */);
                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
-                    intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
-                    intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
                     mContext.startActivity(intent);
                 }
                 mInternetDialogController.connect(wifiEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 8b6ddb4..d1c7844 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -355,8 +355,8 @@
                                 isChecked, false);
                     }
                 });
-        mConnectedWifListLayout.setOnClickListener(v -> onClickConnectedWifi());
-        mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+        mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
+        mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
         mWiFiToggle.setOnCheckedChangeListener(
                 (buttonView, isChecked) -> {
                     if (mWifiManager == null) return;
@@ -519,7 +519,7 @@
         if (TextUtils.isEmpty(mWifiScanNotifyText.getText())) {
             final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
                     AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
-                    v -> mInternetDialogController.launchWifiScanningSetting());
+                    mInternetDialogController::launchWifiScanningSetting);
             mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
                     getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
             mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
@@ -527,15 +527,16 @@
         mWifiScanNotifyLayout.setVisibility(View.VISIBLE);
     }
 
-    void onClickConnectedWifi() {
+    void onClickConnectedWifi(View view) {
         if (mConnectedWifiEntry == null) {
             return;
         }
-        mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey());
+        mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey(),
+                view);
     }
 
-    void onClickSeeMoreButton() {
-        mInternetDialogController.launchNetworkSetting();
+    void onClickSeeMoreButton(View view) {
+        mInternetDialogController.launchNetworkSetting(view);
     }
 
     CharSequence getDialogTitleText() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f89b7a3..b322cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -72,6 +72,7 @@
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -111,7 +112,6 @@
             "android.settings.NETWORK_PROVIDER_SETTINGS";
     private static final String ACTION_WIFI_SCANNING_SETTINGS =
             "android.settings.WIFI_SCANNING_SETTINGS";
-    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
     public static final int NO_CELL_DATA_TYPE_ICON = 0;
     private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
@@ -620,36 +620,32 @@
         return summary;
     }
 
-    void launchNetworkSetting() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-        mCallback.dismissDialog();
+    private void startActivity(Intent intent, View view) {
+        ActivityLaunchAnimator.Controller controller =
+                mDialogLaunchAnimator.createActivityLaunchController(view);
 
-        mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+        if (controller == null) {
+            mCallback.dismissDialog();
+        }
+
+        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
     }
 
-    void launchWifiNetworkDetailsSetting(String key) {
+    void launchNetworkSetting(View view) {
+        startActivity(getSettingsIntent(), view);
+    }
+
+    void launchWifiNetworkDetailsSetting(String key, View view) {
         Intent intent = getWifiDetailsSettingsIntent(key);
         if (intent != null) {
-            // Dismissing a dialog into its touch surface and starting an activity at the same time
-            // looks bad, so let's make sure the dialog just fades out quickly.
-            mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-            mCallback.dismissDialog();
-
-            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+            startActivity(intent, view);
         }
     }
 
-    void launchWifiScanningSetting() {
-        // Dismissing a dialog into its touch surface and starting an activity at the same time
-        // looks bad, so let's make sure the dialog just fades out quickly.
-        mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
-        mCallback.dismissDialog();
-
+    void launchWifiScanningSetting(View view) {
         final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        startActivity(intent, view);
     }
 
     void connectCarrierNetwork() {
@@ -856,8 +852,8 @@
             }
 
             if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
-                final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
-                        .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
+                final Intent intent = WifiUtils.getWifiDialogIntent(mWifiEntry.getKey(),
+                        true /* connectForCaller */);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 mActivityStarter.startActivity(intent, false /* dismissShade */);
             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 8c8c5c8..88aa734 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -19,6 +19,7 @@
 import android.app.Dialog
 import android.content.Context
 import android.content.DialogInterface
+import android.content.DialogInterface.BUTTON_NEUTRAL
 import android.content.Intent
 import android.provider.Settings
 import android.view.LayoutInflater
@@ -84,16 +85,20 @@
             setPositiveButton(R.string.quick_settings_done) { _, _ ->
                 uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
             }
-            setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
+            setNeutralButton(R.string.quick_settings_more_user_settings, { _, _ ->
                 if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                    dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
                     uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
+                    val controller = dialogLaunchAnimator.createActivityLaunchController(
+                        getButton(BUTTON_NEUTRAL))
+
+                    if (controller == null) {
+                        dismiss()
+                    }
+
                     activityStarter.postStartActivityDismissingKeyguard(
-                        USER_SETTINGS_INTENT,
-                        0
-                    )
+                        USER_SETTINGS_INTENT, 0, controller)
                 }
-            }
+            }, false /* dismissOnClick */)
             val gridFrame = LayoutInflater.from(this.context)
                 .inflate(R.layout.qs_user_dialog_content, null)
             setView(gridFrame)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index f982790..4a9a1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -263,22 +263,29 @@
         inoutInfo.touchableRegion.set(getTouchRegion(true));
     }
 
-    private Region getTouchRegion(boolean includeScrim) {
-        Region touchRegion = new Region();
+    private Region getSwipeRegion() {
+        Region swipeRegion = new Region();
 
         final Rect tmpRect = new Rect();
         mScreenshotPreview.getBoundsOnScreen(tmpRect);
         tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
                 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
         mActionsContainerBackground.getBoundsOnScreen(tmpRect);
         tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
                 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
         mDismissButton.getBoundsOnScreen(tmpRect);
-        touchRegion.op(tmpRect, Region.Op.UNION);
+        swipeRegion.op(tmpRect, Region.Op.UNION);
+
+        return swipeRegion;
+    }
+
+    private Region getTouchRegion(boolean includeScrim) {
+        Region touchRegion = getSwipeRegion();
 
         if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) {
+            final Rect tmpRect = new Rect();
             mScrollingScrim.getBoundsOnScreen(tmpRect);
             touchRegion.op(tmpRect, Region.Op.UNION);
         }
@@ -328,7 +335,7 @@
     @Override // ViewGroup
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // scrolling scrim should not be swipeable; return early if we're on the scrim
-        if (!getTouchRegion(false).contains((int) ev.getRawX(), (int) ev.getRawY())) {
+        if (!getSwipeRegion().contains((int) ev.getRawX(), (int) ev.getRawY())) {
             return false;
         }
         // always pass through the down event so the swipe handler knows the initial state
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0509a7c..7f43f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -327,7 +327,9 @@
     private void updateOrganizedOwnedDevice() {
         // avoid calling this method since it has an IPC
         mOrganizationOwnedDevice = whitelistIpcs(this::isOrganizationOwnedDevice);
-        updatePersistentIndications(false, KeyguardUpdateMonitor.getCurrentUser());
+        if (!mDozing) {
+            updateDisclosure();
+        }
     }
 
     private void updateDisclosure() {
@@ -1185,7 +1187,7 @@
                 mTopIndicationView.clearMessages();
                 mRotateTextViewController.clearMessages();
             } else {
-                updatePersistentIndications(false, KeyguardUpdateMonitor.getCurrentUser());
+                updateIndication(false);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index b312ce2..8366bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -67,7 +67,7 @@
     wakefulnessLifecycle: WakefulnessLifecycle,
     configurationController: ConfigurationController,
     falsingManager: FalsingManager,
-    dumpManager: DumpManager,
+    dumpManager: DumpManager
 ) : Dumpable {
     private var pulseHeight: Float = 0f
     private var useSplitShade: Boolean = false
@@ -363,6 +363,7 @@
         notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
         depthController.transitionToFullShadeProgress = scrimProgress
         udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
+        statusbar.setTransitionToFullShadeProgress(scrimProgress)
     }
 
     private fun setDragDownAmountAnimated(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 092e86d..3f101bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -45,7 +45,6 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
 import com.android.systemui.statusbar.notification.collection.render.NotifStats;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -97,7 +96,6 @@
     private final Optional<Bubbles> mBubblesOptional;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final KeyguardBypassController mBypassController;
-    private final ForegroundServiceSectionController mFgsSectionController;
     private final NotifPipelineFlags mNotifPipelineFlags;
     private AssistantFeedbackController mAssistantFeedbackController;
     private final KeyguardStateController mKeyguardStateController;
@@ -129,7 +127,6 @@
             KeyguardBypassController bypassController,
             Optional<Bubbles> bubblesOptional,
             DynamicPrivacyController privacyController,
-            ForegroundServiceSectionController fgsSectionController,
             DynamicChildBindController dynamicChildBindController,
             LowPriorityInflationHelper lowPriorityInflationHelper,
             AssistantFeedbackController assistantFeedbackController,
@@ -145,7 +142,6 @@
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
-        mFgsSectionController = fgsSectionController;
         mNotifPipelineFlags = notifPipelineFlags;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
@@ -417,8 +413,7 @@
                 && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
                         ent.getKey(), ent.getSbn().getGroupKey());
         if (ent.isRowDismissed() || ent.isRowRemoved()
-                || isBubbleNotificationSuppressedFromShade
-                || mFgsSectionController.hasEntry(ent)) {
+                || isBubbleNotificationSuppressedFromShade) {
             // we want to suppress removed notifications because they could
             // temporarily become children if they were isolated before.
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 72c4ce8..0fb6ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -427,15 +427,17 @@
                 typedValue, true);
         float scaleFactor = typedValue.getFloat();
 
-        // We downscale the loaded drawable to reasonable size to protect against applications
-        // using too much memory. The size can be tweaked in config.xml. Drawables
-        // that are already sized properly won't be touched.
-        boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-        Resources res = sysuiContext.getResources();
-        int maxIconSize = res.getDimensionPixelSize(isLowRamDevice
-                                ? com.android.internal.R.dimen.notification_small_icon_size_low_ram
-                                : com.android.internal.R.dimen.notification_small_icon_size);
-        icon = DrawableSize.downscaleToSize(res, icon, maxIconSize, maxIconSize);
+        if (icon != null) {
+            // We downscale the loaded drawable to reasonable size to protect against applications
+            // using too much memory. The size can be tweaked in config.xml. Drawables that are
+            // already sized properly won't be touched.
+            boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
+            Resources res = sysuiContext.getResources();
+            int maxIconSize = res.getDimensionPixelSize(isLowRamDevice
+                    ? com.android.internal.R.dimen.notification_small_icon_size_low_ram
+                    : com.android.internal.R.dimen.notification_small_icon_size);
+            icon = DrawableSize.downscaleToSize(res, icon, maxIconSize, maxIconSize);
+        }
 
         // No need to scale the icon, so return it as is.
         if (scaleFactor == 1.f) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 7811401..02870a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -32,6 +32,7 @@
 import android.text.format.DateFormat;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.view.Choreographer;
 import android.view.InsetsFlags;
 import android.view.InsetsVisibilities;
 import android.view.View;
@@ -44,6 +45,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
@@ -352,8 +354,17 @@
     }
 
     private void beginInteractionJankMonitor() {
+        final boolean shouldPost =
+                (mIsDozing && mDozeAmount == 0) || (!mIsDozing && mDozeAmount == 1);
         if (mInteractionJankMonitor != null && mView != null && mView.isAttachedToWindow()) {
-            mInteractionJankMonitor.begin(mView, getCujType());
+            if (shouldPost) {
+                Choreographer.getInstance().postCallback(
+                        Choreographer.CALLBACK_ANIMATION, this::beginInteractionJankMonitor, null);
+            } else {
+                Configuration.Builder builder = Configuration.Builder.withView(getCujType(), mView)
+                        .setDeferMonitorForAnimationStart(false);
+                mInteractionJankMonitor.begin(builder);
+            }
         }
     }
 
@@ -507,6 +518,10 @@
         pw.println(" mLeaveOpenOnKeyguardHide=" + mLeaveOpenOnKeyguardHide);
         pw.println(" mKeyguardRequested=" + mKeyguardRequested);
         pw.println(" mIsDozing=" + mIsDozing);
+        pw.println(" mListeners{" + mListeners.size() + "}=");
+        for (RankedListener rl : mListeners) {
+            pw.println("    " + rl.mListener);
+        }
         pw.println(" Historical states:");
         // Ignore records without a timestamp
         int size = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index d01fc93..10e90fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -55,7 +55,6 @@
         rippleShader.progress = 0f
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
         ripplePaint.shader = rippleShader
-        visibility = View.GONE
     }
 
     override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -86,12 +85,10 @@
         animator.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationEnd(animation: Animator?) {
                 rippleInProgress = false
-                visibility = View.GONE
                 onAnimationEnd?.run()
             }
         })
         animator.start()
-        visibility = View.VISIBLE
         rippleInProgress = true
     }
 
@@ -100,6 +97,11 @@
     }
 
     override fun onDraw(canvas: Canvas?) {
+        if (canvas == null || !canvas.isHardwareAccelerated) {
+            // Drawing with the ripple shader requires hardware acceleration, so skip
+            // if it's unsupported.
+            return
+        }
         // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
         // the active effect area. Values here should be kept in sync with the
         // animation implementation in the ripple shader.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 842be5b..48717e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -28,15 +28,15 @@
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.Utils
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils
-import com.android.systemui.R
-import com.android.systemui.flags.Flags
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -53,8 +53,8 @@
 @SysUISingleton
 class WiredChargingRippleController @Inject constructor(
     commandRegistry: CommandRegistry,
-    batteryController: BatteryController,
-    configurationController: ConfigurationController,
+    private val batteryController: BatteryController,
+    private val configurationController: ConfigurationController,
     featureFlags: FeatureFlags,
     private val context: Context,
     private val windowManager: WindowManager,
@@ -88,6 +88,11 @@
 
     init {
         pluggedIn = batteryController.isPluggedIn
+        commandRegistry.registerCommand("charging-ripple") { ChargingRippleCommand() }
+        updateRippleColor()
+    }
+
+    fun registerCallbacks() {
         val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
             override fun onBatteryLevelChanged(
                 level: Int,
@@ -123,9 +128,6 @@
             }
         }
         configurationController.addCallback(configurationChangedListener)
-
-        commandRegistry.registerCommand("charging-ripple") { ChargingRippleCommand() }
-        updateRippleColor()
     }
 
     // Lazily debounce ripple to avoid triggering ripple constantly (e.g. from flaky chargers).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 5361a671..2b924a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -34,6 +34,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.R;
+import com.android.systemui.util.Assert;
 
 import java.io.PrintWriter;
 
@@ -190,6 +191,7 @@
     }
 
     private void handleStatusUpdated() {
+        Assert.isMainThread();
         copyWifiStates();
         notifyListenersIfNecessary();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index c687e82..364f36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -60,7 +60,6 @@
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
@@ -198,7 +197,6 @@
             KeyguardBypassController bypassController,
             Optional<Bubbles> bubblesOptional,
             DynamicPrivacyController privacyController,
-            ForegroundServiceSectionController fgsSectionController,
             DynamicChildBindController dynamicChildBindController,
             LowPriorityInflationHelper lowPriorityInflationHelper,
             AssistantFeedbackController assistantFeedbackController,
@@ -217,7 +215,6 @@
                 bypassController,
                 bubblesOptional,
                 privacyController,
-                fgsSectionController,
                 dynamicChildBindController,
                 lowPriorityInflationHelper,
                 assistantFeedbackController,
@@ -261,6 +258,7 @@
     @Provides
     @SysUISingleton
     static OngoingCallController provideOngoingCallController(
+            Context context,
             CommonNotifCollection notifCollection,
             SystemClock systemClock,
             ActivityStarter activityStarter,
@@ -284,6 +282,7 @@
                         : Optional.empty();
         OngoingCallController ongoingCallController =
                 new OngoingCallController(
+                        context,
                         notifCollection,
                         ongoingCallFlags,
                         systemClock,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
new file mode 100644
index 0000000..76766b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.gesture
+
+import android.annotation.CallSuper
+import android.os.Looper
+import android.view.Choreographer
+import android.view.Display
+import android.view.InputEvent
+import com.android.systemui.shared.system.InputChannelCompat
+import com.android.systemui.shared.system.InputMonitorCompat
+
+/**
+ * An abstract class to help detect gestures that occur anywhere on the display (not specific to a
+ * certain view).
+ *
+ * This class handles starting/stopping the gesture detection system as well as
+ * registering/unregistering callbacks for when gestures occur. Note that the class will only listen
+ * for gestures when there's at least one callback registered.
+ *
+ * Subclasses should implement [onInputEvent] to detect their specific gesture. Once a specific
+ * gesture is detected, they should call [onGestureDetected] (which will notify the callbacks).
+ */
+abstract class GenericGestureDetector(
+    private val tag: String
+) {
+    /**
+     * Active callbacks, each associated with a tag. Gestures will only be monitored if
+     * [callbacks.size] > 0.
+     */
+    private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
+
+    private var inputMonitor: InputMonitorCompat? = null
+    private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
+
+    /** Adds a callback that will be triggered when the tap gesture is detected. */
+    fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
+        val callbacksWasEmpty = callbacks.isEmpty()
+        callbacks[tag] = callback
+        if (callbacksWasEmpty) {
+            startGestureListening()
+        }
+    }
+
+    /** Removes the callback. */
+    fun removeOnGestureDetectedCallback(tag: String) {
+        callbacks.remove(tag)
+        if (callbacks.isEmpty()) {
+            stopGestureListening()
+        }
+    }
+
+    /** Triggered each time a touch event occurs (and at least one callback is registered). */
+    abstract fun onInputEvent(ev: InputEvent)
+
+    /** Should be called by subclasses when their specific gesture is detected. */
+    internal fun onGestureDetected() {
+        callbacks.values.forEach { it.invoke() }
+    }
+
+    /** Start listening to touch events. */
+    @CallSuper
+    internal open fun startGestureListening() {
+        stopGestureListening()
+
+        inputMonitor = InputMonitorCompat(tag, Display.DEFAULT_DISPLAY).also {
+            inputReceiver = it.getInputReceiver(
+                Looper.getMainLooper(),
+                Choreographer.getInstance(),
+                this::onInputEvent
+            )
+        }
+    }
+
+    /** Stop listening to touch events. */
+    @CallSuper
+    internal open fun stopGestureListening() {
+        inputMonitor?.let {
+            inputMonitor = null
+            it.dispose()
+        }
+        inputReceiver?.let {
+            inputReceiver = null
+            it.dispose()
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 7cdf69d..fcb285a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -17,15 +17,13 @@
 package com.android.systemui.statusbar.gesture
 
 import android.content.Context
-import android.os.Looper
-import android.view.Choreographer
-import android.view.Display
 import android.view.InputEvent
 import android.view.MotionEvent
-import android.view.MotionEvent.*
+import android.view.MotionEvent.ACTION_CANCEL
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shared.system.InputChannelCompat
-import com.android.systemui.shared.system.InputMonitorCompat
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import javax.inject.Inject
 
@@ -38,43 +36,17 @@
     context: Context,
     private val statusBarWindowController: StatusBarWindowController,
     private val logger: SwipeStatusBarAwayGestureLogger
-) {
-
-    /**
-     * Active callbacks, each associated with a tag. Gestures will only be monitored if
-     * [callbacks.size] > 0.
-     */
-    private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
+) : GenericGestureDetector(SwipeStatusBarAwayGestureHandler::class.simpleName!!) {
 
     private var startY: Float = 0f
     private var startTime: Long = 0L
     private var monitoringCurrentTouch: Boolean = false
 
-    private var inputMonitor: InputMonitorCompat? = null
-    private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
-
     private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
         com.android.internal.R.dimen.system_gestures_start_threshold
     )
 
-    /** Adds a callback that will be triggered when the swipe away gesture is detected. */
-    fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
-        val callbacksWasEmpty = callbacks.isEmpty()
-        callbacks[tag] = callback
-        if (callbacksWasEmpty) {
-            startGestureListening()
-        }
-    }
-
-    /** Removes the callback. */
-    fun removeOnGestureDetectedCallback(tag: String) {
-        callbacks.remove(tag)
-        if (callbacks.isEmpty()) {
-             stopGestureListening()
-        }
-    }
-
-    private fun onInputEvent(ev: InputEvent) {
+    override fun onInputEvent(ev: InputEvent) {
         if (ev !is MotionEvent) {
             return
         }
@@ -108,7 +80,7 @@
                 ) {
                     monitoringCurrentTouch = false
                     logger.logGestureDetected(ev.y.toInt())
-                    callbacks.values.forEach { it.invoke() }
+                    onGestureDetected()
                 }
             }
             ACTION_CANCEL, ACTION_UP -> {
@@ -120,33 +92,15 @@
         }
     }
 
-    /** Start listening for the swipe gesture. */
-    private fun startGestureListening() {
-        stopGestureListening()
-
+    override fun startGestureListening() {
+        super.startGestureListening()
         logger.logInputListeningStarted()
-        inputMonitor = InputMonitorCompat(TAG, Display.DEFAULT_DISPLAY).also {
-            inputReceiver = it.getInputReceiver(
-                Looper.getMainLooper(),
-                Choreographer.getInstance(),
-                this::onInputEvent
-            )
-        }
     }
 
-    /** Stop listening for the swipe gesture. */
-    private fun stopGestureListening() {
-        inputMonitor?.let {
-            logger.logInputListeningStopped()
-            inputMonitor = null
-            it.dispose()
-        }
-        inputReceiver?.let {
-            inputReceiver = null
-            it.dispose()
-        }
+    override fun stopGestureListening() {
+        super.stopGestureListening()
+        logger.logInputListeningStopped()
     }
 }
 
 private const val SWIPE_TIMEOUT_MS: Long = 500
-private val TAG = SwipeStatusBarAwayGestureHandler::class.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
new file mode 100644
index 0000000..4107ce2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.gesture
+
+import android.content.Context
+import android.view.GestureDetector
+import android.view.InputEvent
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class to detect when a user taps the screen. To be notified when the tap is detected, add a
+ * callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+class TapGestureDetector @Inject constructor(
+    private val context: Context
+) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) {
+
+    private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
+        override fun onSingleTapUp(e: MotionEvent?): Boolean {
+            onGestureDetected()
+            return true
+        }
+    }
+
+    private var gestureDetector: GestureDetector? = null
+
+    override fun onInputEvent(ev: InputEvent) {
+        if (ev !is MotionEvent) {
+            return
+        }
+        // Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap
+        // is detected.
+        gestureDetector!!.onTouchEvent(ev)
+    }
+
+    /** Start listening for the tap gesture. */
+    override fun startGestureListening() {
+        super.startGestureListening()
+        gestureDetector = GestureDetector(context, gestureListener)
+    }
+
+    /** Stop listening for the swipe gesture. */
+    override fun stopGestureListening() {
+        super.stopGestureListening()
+        gestureDetector = null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
deleted file mode 100644
index 314051c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
+++ /dev/null
@@ -1,49 +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
-
-import android.content.Context
-import android.provider.DeviceConfig
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.util.DeviceConfigProxy
-import javax.inject.Inject
-
-private var sIsEnabled: Boolean? = null
-
-/**
- * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config.
- */
-// TODO: this is really boilerplatey, make a base class that just wraps the device config
-@SysUISingleton
-class ForegroundServiceDismissalFeatureController @Inject constructor(
-    val proxy: DeviceConfigProxy,
-    val context: Context
-) {
-    fun isForegroundServiceDismissalEnabled(): Boolean {
-        return isEnabled(proxy)
-    }
-}
-
-private fun isEnabled(proxy: DeviceConfigProxy): Boolean {
-    if (sIsEnabled == null) {
-        sIsEnabled = proxy.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_ALLOW_FGS_DISMISSAL, false)
-    }
-
-    return sIsEnabled!!
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f97b936..1d96de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -111,7 +111,6 @@
     private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
     private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
     private final LeakDetector mLeakDetector;
-    private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
     private final IStatusBarService mStatusBarService;
     private final NotifLiveDataStoreImpl mNotifLiveDataStore;
     private final DumpManager mDumpManager;
@@ -159,7 +158,6 @@
             Lazy<NotificationRowBinder> notificationRowBinderLazy,
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
             LeakDetector leakDetector,
-            ForegroundServiceDismissalFeatureController fgsFeatureController,
             IStatusBarService statusBarService,
             NotifLiveDataStoreImpl notifLiveDataStore,
             DumpManager dumpManager
@@ -170,7 +168,6 @@
         mNotificationRowBinderLazy = notificationRowBinderLazy;
         mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
         mLeakDetector = leakDetector;
-        mFgsFeatureController = fgsFeatureController;
         mStatusBarService = statusBarService;
         mNotifLiveDataStore = notifLiveDataStore;
         mDumpManager = dumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
index 6f8e5da..4ebf337 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
@@ -21,8 +21,9 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.util.ListenerSet
 import com.android.systemui.util.concurrency.DelayableExecutor
+import dagger.Binds
 import dagger.Module
-import dagger.Provides
+import javax.inject.Inject
 
 /**
  * Choreographs evaluation resulting from multiple asynchronous sources. Specifically, it exposes
@@ -46,22 +47,21 @@
     fun removeOnEvalListener(onEvalListener: Runnable)
 }
 
+@Module(includes = [PrivateModule::class])
+object NotifPipelineChoreographerModule
+
 @Module
-object NotifPipelineChoreographerModule {
-    @Provides
-    @JvmStatic
-    @SysUISingleton
-    fun provideChoreographer(
-        choreographer: Choreographer,
-        @Main mainExecutor: DelayableExecutor
-    ): NotifPipelineChoreographer = NotifPipelineChoreographerImpl(choreographer, mainExecutor)
+private interface PrivateModule {
+    @Binds
+    fun bindChoreographer(impl: NotifPipelineChoreographerImpl): NotifPipelineChoreographer
 }
 
 private const val TIMEOUT_MS: Long = 100
 
-private class NotifPipelineChoreographerImpl(
+@SysUISingleton
+private class NotifPipelineChoreographerImpl @Inject constructor(
     private val viewChoreographer: Choreographer,
-    private val executor: DelayableExecutor
+    @Main private val executor: DelayableExecutor
 ) : NotifPipelineChoreographer {
 
     private val listeners = ListenerSet<Runnable>()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 9c82cb6..72d0918 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -30,29 +30,24 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import dagger.Binds
 import dagger.Module
-import dagger.Provides
+import javax.inject.Inject
+
+@Module(includes = [PrivateModule::class])
+interface SensitiveContentCoordinatorModule
 
 @Module
-object SensitiveContentCoordinatorModule {
-    @Provides
-    @JvmStatic
-    @CoordinatorScope
-    fun provideCoordinator(
-        dynamicPrivacyController: DynamicPrivacyController,
-        lockscreenUserManager: NotificationLockscreenUserManager,
-        keyguardUpdateMonitor: KeyguardUpdateMonitor,
-        statusBarStateController: StatusBarStateController,
-        keyguardStateController: KeyguardStateController
-    ): SensitiveContentCoordinator =
-            SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager,
-            keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
+private interface PrivateModule {
+    @Binds
+    fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator
 }
 
 /** Coordinates re-inflation and post-processing of sensitive notification content. */
 interface SensitiveContentCoordinator : Coordinator
 
-private class SensitiveContentCoordinatorImpl(
+@CoordinatorScope
+private class SensitiveContentCoordinatorImpl @Inject constructor(
     private val dynamicPrivacyController: DynamicPrivacyController,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
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 e3ebef9..53889f4 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
@@ -38,7 +38,6 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
@@ -128,7 +127,6 @@
             Lazy<NotificationRowBinder> notificationRowBinderLazy,
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
             LeakDetector leakDetector,
-            ForegroundServiceDismissalFeatureController fgsFeatureController,
             IStatusBarService statusBarService,
             NotifLiveDataStoreImpl notifLiveDataStore,
             DumpManager dumpManager) {
@@ -139,7 +137,6 @@
                 notificationRowBinderLazy,
                 notificationRemoteInputManagerLazy,
                 leakDetector,
-                fgsFeatureController,
                 statusBarService,
                 notifLiveDataStore,
                 dumpManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
deleted file mode 100644
index dbfa27f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ /dev/null
@@ -1,43 +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.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-
-class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
-    var entry: NotificationEntry? = null
-        set(value) {
-            field = value
-            update()
-        }
-
-    private fun update() {
-        (findViewById(R.id.app_name) as TextView).apply {
-            text = entry?.row?.appName
-        }
-
-        (findViewById(R.id.icon) as StatusBarIconView).apply {
-            set(entry?.icons?.statusBarIcon?.statusBarIcon)
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 06b739b..c2c40d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -17,6 +17,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.widget.Toast.LENGTH_SHORT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -33,13 +38,15 @@
 import android.util.Log;
 import android.view.DragEvent;
 import android.view.HapticFeedbackConstants;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.widget.ImageView;
+import android.widget.Toast;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -55,12 +62,15 @@
 
     private final Context mContext;
     private final HeadsUpManager mHeadsUpManager;
+    private final ShadeController mShadeController;
 
     @Inject
     public ExpandableNotificationRowDragController(Context context,
-            HeadsUpManager headsUpManager) {
+            HeadsUpManager headsUpManager,
+            ShadeController shadeController) {
         mContext = context;
         mHeadsUpManager = headsUpManager;
+        mShadeController = shadeController;
 
         init();
     }
@@ -87,6 +97,16 @@
         final PendingIntent contentIntent = notification.contentIntent != null
                 ? notification.contentIntent
                 : notification.fullScreenIntent;
+        if (contentIntent == null) {
+            if (!enr.isPinned()) {
+                // We dismiss the shade for consistency, but also because toasts currently don't
+                // show above the shade
+                dismissShade();
+            }
+            Toast.makeText(mContext, R.string.drag_split_not_supported, LENGTH_SHORT)
+                 .show();
+            return;
+        }
         Bitmap iconBitmap = getBitmapFromDrawable(
                 getPkgIcon(enr.getEntry().getSbn().getPackageName()));
 
@@ -97,15 +117,30 @@
         ClipDescription clipDescription = new ClipDescription("Drag And Drop",
                 new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
         Intent dragIntent = new Intent();
-        dragIntent.putExtra("android.intent.extra.PENDING_INTENT", contentIntent);
+        dragIntent.putExtra(ClipDescription.EXTRA_PENDING_INTENT, contentIntent);
         dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
         ClipData.Item item = new ClipData.Item(dragIntent);
         ClipData dragData = new ClipData(clipDescription, item);
         View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
         view.setOnDragListener(getDraggedViewDragListener());
-        view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL);
+        boolean result = view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL
+                | View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
+        if (result) {
+            view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            if (enr.isPinned()) {
+                mHeadsUpManager.releaseAllImmediately();
+            } else {
+                dismissShade();
+            }
+        }
     }
 
+    private void dismissShade() {
+        // Speed up dismissing the shade since the drag needs to be handled by
+        // the shell layer underneath
+        mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
+                false /* delayed */, 1.1f /* speedUpFactor */);
+    }
 
     private Drawable getPkgIcon(String pkgName) {
         Drawable pkgicon = null;
@@ -145,16 +180,6 @@
         return (view, dragEvent) -> {
             switch (dragEvent.getAction()) {
                 case DragEvent.ACTION_DRAG_STARTED:
-                    view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                    if (view instanceof ExpandableNotificationRow) {
-                        ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
-                        if (enr.isPinned()) {
-                            mHeadsUpManager.releaseAllImmediately();
-                        } else {
-                            Dependency.get(ShadeController.class).animateCollapsePanels(
-                                    CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
-                        }
-                    }
                     return true;
                 case DragEvent.ACTION_DRAG_ENDED:
                     if (dragEvent.getResult()) {
@@ -162,10 +187,55 @@
                             ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
                             enr.dragAndDropSuccess();
                         }
+                    } else {
+                        // Fade out the drag surface in place instead of animating back to the
+                        // start position now that the shade is closed
+                        fadeOutAndRemoveDragSurface(dragEvent);
                     }
+                    // Clear the drag listener set above
+                    view.setOnDragListener(null);
                     return true;
             }
             return false;
         };
     }
+
+    private void fadeOutAndRemoveDragSurface(DragEvent dragEvent) {
+        SurfaceControl dragSurface = dragEvent.getDragSurface();
+        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        ValueAnimator returnAnimator = ValueAnimator.ofFloat(0f, 1f);
+        returnAnimator.setDuration(200);
+        returnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        returnAnimator.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            float alpha = 1f - t;
+            tx.setAlpha(dragSurface, alpha);
+            tx.apply();
+        });
+        returnAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCanceled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                cleanUpSurface();
+                mCanceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCanceled) {
+                    // Already handled above
+                    return;
+                }
+                cleanUpSurface();
+            }
+
+            private void cleanUpSurface() {
+                tx.remove(dragSurface);
+                tx.apply();
+                tx.close();
+            }
+        });
+        returnAnimator.start();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
deleted file mode 100644
index 17396ad..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
+++ /dev/null
@@ -1,38 +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.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-
-import com.android.systemui.R
-
-class ForegroundServiceDungeonView(context: Context, attrs: AttributeSet)
-    : StackScrollerDecorView(context, attrs) {
-    override fun findContentView(): View? {
-        return findViewById(R.id.foreground_service_dungeon)
-    }
-
-    override fun findSecondaryView(): View? {
-        return null
-    }
-
-    override fun setVisible(visible: Boolean, animate: Boolean) {
-        // Visibility is controlled by the ForegroundServiceSectionController
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
deleted file mode 100644
index 75ca337..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
+++ /dev/null
@@ -1,168 +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.content.Context
-import android.service.notification.NotificationListenerService.REASON_CANCEL
-import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
-import android.service.notification.NotificationListenerService.REASON_CLICK
-import android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController
-import com.android.systemui.statusbar.notification.NotificationEntryListener
-import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.DungeonRow
-import com.android.systemui.util.Assert
-import javax.inject.Inject
-
-/**
- * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground
- * service notifications and can reinstantiate them when requested.
- */
-@SysUISingleton
-class ForegroundServiceSectionController @Inject constructor(
-    val entryManager: NotificationEntryManager,
-    val featureController: ForegroundServiceDismissalFeatureController
-) {
-    private val TAG = "FgsSectionController"
-    private var context: Context? = null
-
-    private val entries = mutableSetOf<NotificationEntry>()
-
-    private var entriesView: View? = null
-
-    init {
-        if (featureController.isForegroundServiceDismissalEnabled()) {
-            entryManager.addNotificationRemoveInterceptor(this::shouldInterceptRemoval)
-
-            entryManager.addNotificationEntryListener(object : NotificationEntryListener {
-                override fun onPostEntryUpdated(entry: NotificationEntry) {
-                    if (entries.contains(entry)) {
-                        removeEntry(entry)
-                        addEntry(entry)
-                        update()
-                    }
-                }
-            })
-        }
-    }
-
-    private fun shouldInterceptRemoval(
-        key: String,
-        entry: NotificationEntry?,
-        reason: Int
-    ): Boolean {
-        Assert.isMainThread()
-        val isClearAll = reason == REASON_CANCEL_ALL
-        val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
-        // REASON_APP_CANCEL and REASON_APP_CANCEL_ALL are ignored, because the
-        // foreground service associated with it is gone.
-        val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
-
-        if (entry == null) return false
-
-        // We only want to retain notifications that the user dismissed
-        // TODO: centralize the entry.isClearable logic and this so that it's clear when a notif is
-        // clearable
-        if (isUserDismiss && !entry.sbn.isClearable) {
-            if (!hasEntry(entry)) {
-                addEntry(entry)
-                update()
-            }
-            // TODO: This isn't ideal. Slightly better would at least be to have NEM update the
-            // notif list when an entry gets intercepted
-            entryManager.updateNotifications(
-                    "FgsSectionController.onNotificationRemoveRequested")
-            return true
-        } else if ((isClearAll || isSummaryCancel) && !entry.sbn.isClearable) {
-            // In the case where a FGS notification is part of a group that is cleared or a clear
-            // all, we actually want to stop its removal but also not put it into the dungeon
-            return true
-        } else if (hasEntry(entry)) {
-            removeEntry(entry)
-            update()
-            return false
-        }
-
-        return false
-    }
-
-    private fun removeEntry(entry: NotificationEntry) {
-        Assert.isMainThread()
-        entries.remove(entry)
-    }
-
-    private fun addEntry(entry: NotificationEntry) {
-        Assert.isMainThread()
-        entries.add(entry)
-    }
-
-    fun hasEntry(entry: NotificationEntry): Boolean {
-        Assert.isMainThread()
-        return entries.contains(entry)
-    }
-
-    fun initialize(context: Context) {
-        this.context = context
-    }
-
-    fun createView(li: LayoutInflater): View {
-        entriesView = li.inflate(R.layout.foreground_service_dungeon, null)
-        // Start out gone
-        entriesView!!.visibility = View.GONE
-        return entriesView!!
-    }
-
-    private fun update() {
-        Assert.isMainThread()
-        if (entriesView == null) {
-            throw IllegalStateException("ForegroundServiceSectionController is trying to show " +
-                    "dismissed fgs notifications without having been initialized!")
-        }
-
-        // TODO: these views should be recycled and not inflating on the main thread
-        (entriesView!!.findViewById(R.id.entry_list) as LinearLayout).apply {
-            removeAllViews()
-            entries.sortedBy { it.ranking.rank }.forEach { entry ->
-                val child = LayoutInflater.from(context)
-                        .inflate(R.layout.foreground_service_dungeon_row, null) as DungeonRow
-
-                child.entry = entry
-                child.setOnClickListener {
-                    removeEntry(child.entry!!)
-                    update()
-                    entry.row.unDismiss()
-                    entry.row.resetTranslation()
-                    entryManager.updateNotifications("ForegroundServiceSectionController.onClick")
-                }
-
-                addView(child)
-            }
-        }
-
-        if (entries.isEmpty()) {
-            entriesView?.visibility = View.GONE
-        } else {
-            entriesView?.visibility = View.VISIBLE
-        }
-    }
-}
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 2c4db77..f8096437 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
@@ -100,7 +100,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -453,7 +452,6 @@
     private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
 
     private final NotificationSectionsManager mSectionsManager;
-    private ForegroundServiceDungeonView mFgsSectionView;
     private boolean mAnimateBottomOnLayout;
     private float mLastSentAppear;
     private float mLastSentExpandedHeight;
@@ -614,14 +612,6 @@
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
-    void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
-        if (mFgsSectionView != null) {
-            return;
-        }
-        mFgsSectionView = fgsSectionView;
-        addView(mFgsSectionView, -1);
-    }
-
     /**
      * Set the overexpansion of the panel to be applied to the view.
      */
@@ -5286,9 +5276,6 @@
         // incremented in the following "changeViewPosition" calls so that its value is correct for
         // subsequent calls.
         int offsetFromEnd = 1;
-        if (mFgsSectionView != null) {
-            changeViewPosition(mFgsSectionView, getChildCount() - offsetFromEnd++);
-        }
         changeViewPosition(mFooterView, getChildCount() - offsetFromEnd++);
         changeViewPosition(mEmptyShadeView, getChildCount() - offsetFromEnd++);
 
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 d1c63e3..9b4f8b8 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
@@ -86,7 +86,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -109,7 +108,6 @@
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
@@ -170,8 +168,6 @@
     private final NotificationEntryManager mNotificationEntryManager;
     private final IStatusBarService mIStatusBarService;
     private final UiEventLogger mUiEventLogger;
-    private final ForegroundServiceDismissalFeatureController mFgFeatureController;
-    private final ForegroundServiceSectionController mFgServicesSectionController;
     private final LayoutInflater mLayoutInflater;
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisualStabilityManager mVisualStabilityManager;
@@ -660,8 +656,6 @@
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             IStatusBarService iStatusBarService,
             UiEventLogger uiEventLogger,
-            ForegroundServiceDismissalFeatureController fgFeatureController,
-            ForegroundServiceSectionController fgServicesSectionController,
             LayoutInflater layoutInflater,
             NotificationRemoteInputManager remoteInputManager,
             VisualStabilityManager visualStabilityManager,
@@ -709,8 +703,6 @@
         mNotificationEntryManager = notificationEntryManager;
         mIStatusBarService = iStatusBarService;
         mUiEventLogger = uiEventLogger;
-        mFgFeatureController = fgFeatureController;
-        mFgServicesSectionController = fgServicesSectionController;
         mLayoutInflater = layoutInflater;
         mRemoteInputManager = remoteInputManager;
         mVisualStabilityManager = visualStabilityManager;
@@ -744,12 +736,6 @@
         mNotificationRoundnessManager.setShouldRoundPulsingViews(
                 !mKeyguardBypassController.getBypassEnabled());
 
-        if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
-            mView.initializeForegroundServiceSection(
-                    (ForegroundServiceDungeonView) mFgServicesSectionController.createView(
-                            mLayoutInflater));
-        }
-
         mSwipeHelper = mNotificationSwipeHelperBuilder
                 .setSwipeDirection(SwipeHelper.X)
                 .setNotificationCallback(mNotificationCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 3a3f581..952cd9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -202,10 +202,12 @@
             float newHeight = state.height;
             float newNotificationEnd = newYTranslation + newHeight;
             boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
+            final boolean shadeClosedWithHUN =
+                    ambientState.isShadeOpening() && !ambientState.isShadeExpanded();
             if (mClipNotificationScrollToTop
                     && (!state.inShelf || (isHeadsUp && !firstHeadsUp))
                     && newYTranslation < clipStart
-                    && !ambientState.isShadeOpening()) {
+                    && shadeClosedWithHUN) {
                 // The previous view is overlapping on top, clip!
                 float overlapAmount = clipStart - newYTranslation;
                 state.clipTopAmount = (int) overlapAmount;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index b35e684..05fba54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,6 +28,8 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.assist.AssistManager;
@@ -46,8 +48,11 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import java.util.ArrayList;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -73,6 +78,8 @@
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final DeviceProvisionedController mDeviceProvisionedController;
+    @Nullable
+    private final FoldAodAnimationController mFoldAodAnimationController;
     private final HeadsUpManagerPhone mHeadsUpManagerPhone;
     private final BatteryController mBatteryController;
     private final ScrimController mScrimController;
@@ -105,6 +112,7 @@
             Lazy<AssistManager> assistManagerLazy,
             DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
             PulseExpansionHandler pulseExpansionHandler,
+            Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
             NotificationShadeWindowController notificationShadeWindowController,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             AuthController authController,
@@ -128,6 +136,8 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mAuthController = authController;
         mNotificationIconAreaController = notificationIconAreaController;
+        mFoldAodAnimationController = sysUIUnfoldComponent
+                .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
     }
 
     // TODO: we should try to not pass status bar in here if we can avoid it.
@@ -214,6 +224,10 @@
         }
 
         mStatusBarStateController.setIsDozing(dozing);
+        mNotificationShadeWindowViewController.setDozing(dozing);
+        if (mFoldAodAnimationController != null) {
+            mFoldAodAnimationController.setIsDozing(dozing);
+        }
     }
 
     @Override
@@ -294,6 +308,7 @@
     public void dozeTimeTick() {
         mNotificationPanel.dozeTimeTick();
         mAuthController.dozeTimeTick();
+        mNotificationShadeWindowViewController.dozeTimeTick();
         if (mAmbientIndicationContainer instanceof DozeReceiver) {
             ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
         }
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 d93c013..a0f8d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -135,9 +135,6 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.idle.IdleHostView;
-import com.android.systemui.idle.IdleHostViewController;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
@@ -322,7 +319,6 @@
     private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
     private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
-    private final IdleViewComponent.Factory mIdleViewComponentFactory;
     private final FragmentService mFragmentService;
     private final ScrimController mScrimController;
     private final PrivacyDotViewController mPrivacyDotViewController;
@@ -356,8 +352,6 @@
     @Nullable
     private CommunalHostViewController mCommunalViewController;
     private KeyguardStatusViewController mKeyguardStatusViewController;
-    @Nullable
-    private IdleHostViewController mIdleHostViewController;
     private LockIconViewController mLockIconViewController;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private NotificationsQSContainerController mNotificationsQSContainerController;
@@ -369,7 +363,6 @@
     private VelocityTracker mQsVelocityTracker;
     private boolean mQsTracking;
 
-    private IdleHostView mIdleHostView;
     private CommunalHostView mCommunalView;
 
     /**
@@ -768,7 +761,6 @@
             KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
             CommunalViewComponent.Factory communalViewComponentFactory,
-            IdleViewComponent.Factory idleViewComponentFactory,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             NotificationGroupManagerLegacy groupManager,
             NotificationIconAreaController notificationIconAreaController,
@@ -839,7 +831,6 @@
         mCommunalViewComponentFactory = communalViewComponentFactory;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
-        mIdleViewComponentFactory = idleViewComponentFactory;
         mDepthController = notificationShadeDepthController;
         mContentResolver = contentResolver;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
@@ -966,7 +957,6 @@
     private void onFinishInflate() {
         loadDimens();
         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
-        mIdleHostView = mView.findViewById(R.id.idle_host_view);
         mCommunalView = mView.findViewById(R.id.communal_host);
 
         FrameLayout userAvatarContainer = null;
@@ -989,12 +979,6 @@
                         .getKeyguardStatusBarViewController();
         mKeyguardStatusBarViewController.init();
 
-        if (mIdleHostView != null) {
-            IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(mIdleHostView);
-            mIdleHostViewController = idleViewComponent.getIdleHostViewController();
-            mIdleHostViewController.init();
-        }
-
         if (mCommunalView != null) {
             CommunalViewComponent communalViewComponent =
                     mCommunalViewComponentFactory.build(mCommunalView);
@@ -1008,7 +992,6 @@
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarContainer,
                 keyguardUserSwitcherView,
-                mIdleHostView,
                 mCommunalView);
 
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -1101,7 +1084,6 @@
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView,
-            IdleHostView idleHostView,
             CommunalHostView communalView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
@@ -1109,13 +1091,6 @@
         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
         mKeyguardStatusViewController.init();
 
-        if (idleHostView != null && idleHostView != mIdleHostView) {
-            mIdleHostView = idleHostView;
-            IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
-            mIdleHostViewController = idleViewComponent.getIdleHostViewController();
-            mIdleHostViewController.init();
-        }
-
         if (mKeyguardUserSwitcherController != null) {
             // Try to close the switcher so that callbacks are triggered if necessary.
             // Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1287,7 +1262,7 @@
                         showKeyguardUserSwitcher /* enabled */);
 
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
+                keyguardUserSwitcherView, mCommunalView);
 
         // Update keyguard bottom area
         int index = mView.indexOfChild(mKeyguardBottomArea);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 4e2eb6a..396703b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -36,6 +36,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.lowlightclock.LowLightClockController;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -49,6 +50,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -84,6 +86,7 @@
     private final DockManager mDockManager;
     private final NotificationPanelViewController mNotificationPanelViewController;
     private final PanelExpansionStateManager mPanelExpansionStateManager;
+    private final Optional<LowLightClockController> mLowLightClockController;
 
     private boolean mIsTrackingBarGesture = false;
 
@@ -101,7 +104,8 @@
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             StatusBarWindowStateController statusBarWindowStateController,
-            LockIconViewController lockIconViewController) {
+            LockIconViewController lockIconViewController,
+            Optional<LowLightClockController> lowLightClockController) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
         mTunerService = tunerService;
@@ -115,6 +119,7 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mStatusBarWindowStateController = statusBarWindowStateController;
         mLockIconViewController = lockIconViewController;
+        mLowLightClockController = lowLightClockController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -171,6 +176,8 @@
                 };
         mGestureDetector = new GestureDetector(mView.getContext(), gestureListener);
 
+        mLowLightClockController.ifPresent(controller -> controller.attachLowLightClockView(mView));
+
         mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
             @Override
             public Boolean handleDispatchTouchEvent(MotionEvent ev) {
@@ -450,6 +457,21 @@
         mNotificationShadeWindowController = controller;
     }
 
+    /**
+     * Tell the controller that dozing has begun or ended.
+     * @param dozing True if dozing has begun.
+     */
+    public void setDozing(boolean dozing) {
+        mLowLightClockController.ifPresent(controller -> controller.showLowLightClock(dozing));
+    }
+
+    /**
+     * Tell the controller to perform burn-in prevention.
+     */
+    public void dozeTimeTick() {
+        mLowLightClockController.ifPresent(LowLightClockController::dozeTimeTick);
+    }
+
     @VisibleForTesting
     void setDragDownHelper(DragDownHelper dragDownHelper) {
         mDragDownHelper = dragDownHelper;
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 f13334e..82e0e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -203,6 +203,7 @@
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.charging.WiredChargingRippleController;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -343,6 +344,7 @@
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+    private float mTransitionToFullShadeProgress = 0f;
 
     void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
         updateBubblesVisibility();
@@ -786,7 +788,8 @@
             NotifPipelineFlags notifPipelineFlags,
             InteractionJankMonitor jankMonitor,
             DeviceStateManager deviceStateManager,
-            DreamOverlayStateController dreamOverlayStateController) {
+            DreamOverlayStateController dreamOverlayStateController,
+            WiredChargingRippleController wiredChargingRippleController) {
         super(context);
         mNotificationsController = notificationsController;
         mFragmentService = fragmentService;
@@ -912,6 +915,7 @@
 
         deviceStateManager.registerCallback(mMainExecutor,
                 new FoldStateListener(mContext, this::onFoldedStateChanged));
+        wiredChargingRippleController.registerCallbacks();
     }
 
     @Override
@@ -2580,9 +2584,9 @@
             return controllerFromStatusBar.get();
         }
 
-        if (dismissShade && rootView == mNotificationShadeWindowView) {
-            // We are animating a view in the shade. We have to make sure that we collapse it when
-            // the animation ends or is cancelled.
+        if (dismissShade) {
+            // If the view is not in the status bar, then we are animating a view in the shade.
+            // We have to make sure that we collapse it when the animation ends or is cancelled.
             return new StatusBarLaunchAnimatorController(animationController, this,
                     true /* isLaunchForActivity */);
         }
@@ -3774,6 +3778,15 @@
         updateScrimController();
     }
 
+    /**
+     * Set the amount of progress we are currently in if we're transitioning to the full shade.
+     * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+     * shade.
+     */
+    public void setTransitionToFullShadeProgress(float transitionToFullShadeProgress) {
+        mTransitionToFullShadeProgress = transitionToFullShadeProgress;
+    }
+
     @VisibleForTesting
     public void updateScrimController() {
         Trace.beginSection("StatusBar#updateScrimController");
@@ -3792,7 +3805,8 @@
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
 
         if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
-            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED) {
+            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f) {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
             } else {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d42a423..11d9c31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -68,10 +68,13 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -87,7 +90,7 @@
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
         PanelExpansionListener, NavigationModeController.ModeChangedListener,
-        KeyguardViewController {
+        KeyguardViewController, FoldAodAnimationController.FoldAodAnimationStatus {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -113,6 +116,8 @@
     private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
     private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     private final DreamOverlayStateController mDreamOverlayStateController;
+    @Nullable
+    private final FoldAodAnimationController mFoldAodAnimationController;
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
     private final Lazy<ShadeController> mShadeController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -186,6 +191,7 @@
     private boolean mPulsing;
     private boolean mGesturalNav;
     private boolean mIsDocked;
+    private boolean mScreenOffAnimationPlaying;
 
     protected boolean mFirstUpdate = true;
     protected boolean mLastShowing;
@@ -199,6 +205,7 @@
     private boolean mLastIsDocked;
     private boolean mLastPulsing;
     private int mLastBiometricMode;
+    private boolean mLastScreenOffAnimationPlaying;
     private boolean mQsExpanded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
@@ -246,6 +253,7 @@
             NotificationMediaManager notificationMediaManager,
             KeyguardBouncer.Factory keyguardBouncerFactory,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
+            Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
             Lazy<ShadeController> shadeController,
             LatencyTracker latencyTracker) {
         mContext = context;
@@ -264,6 +272,8 @@
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
         mShadeController = shadeController;
         mLatencyTracker = latencyTracker;
+        mFoldAodAnimationController = sysUIUnfoldComponent
+                .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
     }
 
     @Override
@@ -317,6 +327,9 @@
         mConfigurationController.addCallback(this);
         mGesturalNav = QuickStepContract.isGesturalMode(
                 mNavigationModeController.addListener(this));
+        if (mFoldAodAnimationController != null) {
+            mFoldAodAnimationController.addCallback(this);
+        }
         if (mDockManager != null) {
             mDockManager.addListener(mDockEventListener);
             mIsDocked = mDockManager.isDocked();
@@ -965,6 +978,10 @@
     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
         @Override
         public void run() {
+            NavigationBarView view = mStatusBar.getNavigationBarView();
+            if (view != null) {
+                view.setVisibility(View.VISIBLE);
+            }
             mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
                     .show(navigationBars());
         }
@@ -1019,6 +1036,7 @@
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
         mLastPulsing = mPulsing;
+        mLastScreenOffAnimationPlaying = mScreenOffAnimationPlaying;
         mLastBiometricMode = mBiometricUnlockController.getMode();
         mLastGesturalNav = mGesturalNav;
         mLastIsDocked = mIsDocked;
@@ -1054,14 +1072,15 @@
     /**
      * @return Whether the navigation bar should be made visible based on the current state.
      */
-    protected boolean isNavBarVisible() {
-        int biometricMode = mBiometricUnlockController.getMode();
+    public boolean isNavBarVisible() {
+        boolean isWakeAndUnlockPulsing = mBiometricUnlockController != null
+                && mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
         boolean keyguardShowing = mShowing && !mOccluded;
-        boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing;
         boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
                 && mGesturalNav;
-        return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
-                || mRemoteInputActive || keyguardWithGestureNav
+        return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
+                || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav
                 || mGlobalActionsVisible);
     }
 
@@ -1073,8 +1092,8 @@
         boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
         boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
                 || mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
-        return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
-                || mLastRemoteInputActive || keyguardWithGestureNav
+        return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying
+                || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav
                 || mLastGlobalActionsVisible);
     }
 
@@ -1224,6 +1243,13 @@
         setDozing(isDozing);
     }
 
+    @Override
+    public void onFoldToAodAnimationChanged() {
+        if (mFoldAodAnimationController != null) {
+            mScreenOffAnimationPlaying = mFoldAodAnimationController.shouldPlayAnimation();
+        }
+    }
+
     /**
      * Whether qs is currently expanded.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 2ba37c2..09fca100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.phone
 
+import android.view.View
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.animation.LaunchAnimator
 
@@ -12,6 +13,11 @@
     private val statusBar: StatusBar,
     private val isLaunchForActivity: Boolean = true
 ) : ActivityLaunchAnimator.Controller by delegate {
+    // Always sync the opening window with the shade, given that we draw a hole punch in the shade
+    // of the same size and position as the opening app to make it visible.
+    override val openingWindowSyncView: View?
+        get() = statusBar.notificationShadeWindowView
+
     override fun onIntentStarted(willAnimate: Boolean) {
         delegate.onIntentStarted(willAnimate)
         if (!willAnimate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 6e1ec9c..79d646c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -42,7 +42,13 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Base class for dialogs that should appear over panels and keyguard.
@@ -61,35 +67,25 @@
     private final Context mContext;
     @Nullable private final DismissReceiver mDismissReceiver;
     private final Handler mHandler = new Handler();
-    @Nullable private final SystemUIDialogManager mDialogManager;
+    private final SystemUIDialogManager mDialogManager;
+    private final SysUiState mSysUiState;
 
     private int mLastWidth = Integer.MIN_VALUE;
     private int mLastHeight = Integer.MIN_VALUE;
     private int mLastConfigurationWidthDp = -1;
     private int mLastConfigurationHeightDp = -1;
 
+    private List<Runnable> mOnCreateRunnables = new ArrayList<>();
+
     public SystemUIDialog(Context context) {
         this(context, R.style.Theme_SystemUI_Dialog);
     }
 
-    public SystemUIDialog(Context context, SystemUIDialogManager dialogManager) {
-        this(context, R.style.Theme_SystemUI_Dialog, true, dialogManager);
-    }
-
     public SystemUIDialog(Context context, int theme) {
         this(context, theme, true /* dismissOnDeviceLock */);
     }
 
-    public SystemUIDialog(Context context, int theme, SystemUIDialogManager dialogManager) {
-        this(context, theme, true /* dismissOnDeviceLock */, dialogManager);
-    }
-
     public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
-        this(context, theme, dismissOnDeviceLock, null);
-    }
-
-    public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
-            @Nullable SystemUIDialogManager dialogManager) {
         super(context, theme);
         mContext = context;
 
@@ -99,7 +95,12 @@
         getWindow().setAttributes(attrs);
 
         mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
-        mDialogManager = dialogManager;
+
+        // TODO(b/219008720): Remove those calls to Dependency.get by introducing a
+        // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
+        // the content and attach listeners.
+        mDialogManager = Dependency.get(SystemUIDialogManager.class);
+        mSysUiState = Dependency.get(SysUiState.class);
     }
 
     @Override
@@ -110,6 +111,10 @@
         mLastConfigurationWidthDp = config.screenWidthDp;
         mLastConfigurationHeightDp = config.screenHeightDp;
         updateWindowSize();
+
+        for (int i = 0; i < mOnCreateRunnables.size(); i++) {
+            mOnCreateRunnables.get(i).run();
+        }
     }
 
     private void updateWindowSize() {
@@ -165,13 +170,11 @@
             mDismissReceiver.register();
         }
 
-        if (mDialogManager != null) {
-            mDialogManager.setShowing(this, true);
-        }
-
         // Listen for configuration changes to resize this dialog window. This is mostly necessary
         // for foldables that often go from large <=> small screen when folding/unfolding.
         ViewRootImpl.addConfigCallback(this);
+        mDialogManager.setShowing(this, true);
+        mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true);
     }
 
     @Override
@@ -182,11 +185,9 @@
             mDismissReceiver.unregister();
         }
 
-        if (mDialogManager != null) {
-            mDialogManager.setShowing(this, false);
-        }
-
         ViewRootImpl.removeConfigCallback(this);
+        mDialogManager.setShowing(this, false);
+        mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false);
     }
 
     public void setShowForAllUsers(boolean show) {
@@ -197,16 +198,67 @@
         setMessage(mContext.getString(resId));
     }
 
+    /**
+     * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setPositiveButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_POSITIVE, mContext.getString(resId), onClick);
+        setPositiveButton(resId, onClick, true /* dismissOnClick */);
     }
 
+    /**
+     * Set a listener to be invoked when the positive button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setPositiveButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_POSITIVE, resId, onClick, dismissOnClick);
+    }
+
+    /**
+     * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setNegativeButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
+        setNegativeButton(resId, onClick, true /* dismissOnClick */);
     }
 
+    /**
+     * Set a listener to be invoked when the negative button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setNegativeButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_NEGATIVE, resId, onClick, dismissOnClick);
+    }
+
+    /**
+     * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog
+     * will automatically be dismissed when the button is clicked.
+     */
     public void setNeutralButton(int resId, OnClickListener onClick) {
-        setButton(BUTTON_NEUTRAL, mContext.getString(resId), onClick);
+        setNeutralButton(resId, onClick, true /* dismissOnClick */);
+    }
+
+    /**
+     * Set a listener to be invoked when the neutral button of the dialog is pressed. The dialog
+     * will be dismissed when the button is clicked iff {@code dismissOnClick} is true.
+     */
+    public void setNeutralButton(int resId, OnClickListener onClick, boolean dismissOnClick) {
+        setButton(BUTTON_NEUTRAL, resId, onClick, dismissOnClick);
+    }
+
+    private void setButton(int whichButton, int resId, OnClickListener onClick,
+            boolean dismissOnClick) {
+        if (dismissOnClick) {
+            setButton(whichButton, mContext.getString(resId), onClick);
+        } else {
+            // Set a null OnClickListener to make sure the button is still created and shown.
+            setButton(whichButton, mContext.getString(resId), (OnClickListener) null);
+
+            // When the dialog is created, set the click listener but don't dismiss the dialog when
+            // it is clicked.
+            mOnCreateRunnables.add(() -> getButton(whichButton).setOnClickListener(
+                    view -> onClick.onClick(this, whichButton)));
+        }
     }
 
     public static void setShowForAllUsers(Dialog dialog, boolean show) {
@@ -341,10 +393,13 @@
         private final Dialog mDialog;
         private boolean mRegistered;
         private final BroadcastDispatcher mBroadcastDispatcher;
+        private final DialogLaunchAnimator mDialogLaunchAnimator;
 
         DismissReceiver(Dialog dialog) {
             mDialog = dialog;
+            // TODO(b/219008720): Remove those calls to Dependency.get.
             mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
+            mDialogLaunchAnimator = Dependency.get(DialogLaunchAnimator.class);
         }
 
         void register() {
@@ -361,6 +416,10 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
+            // These broadcast are usually received when locking the device, swiping up to home
+            // (which collapses the shade), etc. In those cases, we usually don't want to animate
+            // back into the view.
+            mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
             mDialog.dismiss();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 83bdd1b..c6b5b1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.charging.WiredChargingRippleController;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -233,7 +234,8 @@
             NotifPipelineFlags notifPipelineFlags,
             InteractionJankMonitor jankMonitor,
             DeviceStateManager deviceStateManager,
-            DreamOverlayStateController dreamOverlayStateController) {
+            DreamOverlayStateController dreamOverlayStateController,
+            WiredChargingRippleController wiredChargingRippleController) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -330,7 +332,8 @@
                 notifPipelineFlags,
                 jankMonitor,
                 deviceStateManager,
-                dreamOverlayStateController
+                dreamOverlayStateController,
+                wiredChargingRippleController
         );
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index c7f7258..0abcaaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -21,6 +21,7 @@
 import android.app.IUidObserver
 import android.app.Notification
 import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
+import android.content.Context
 import android.content.Intent
 import android.util.Log
 import android.view.View
@@ -52,6 +53,7 @@
  */
 @SysUISingleton
 class OngoingCallController @Inject constructor(
+    private val context: Context,
     private val notifCollection: CommonNotifCollection,
     private val ongoingCallFlags: OngoingCallFlags,
     private val systemClock: SystemClock,
@@ -67,13 +69,10 @@
     private var isFullscreen: Boolean = false
     /** Non-null if there's an active call notification. */
     private var callNotificationInfo: CallNotificationInfo? = null
-    /** True if the application managing the call is visible to the user. */
-    private var isCallAppVisible: Boolean = false
     private var chipView: View? = null
-    private var uidObserver: IUidObserver.Stub? = null
 
     private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
-
+    private val uidObserver = CallAppUidObserver()
     private val notifListener = object : NotifCollectionListener {
         // Temporary workaround for b/178406514 for testing purposes.
         //
@@ -158,7 +157,7 @@
     fun hasOngoingCall(): Boolean {
         return callNotificationInfo?.isOngoing == true &&
                 // When the user is in the phone app, don't show the chip.
-                !isCallAppVisible
+                !uidObserver.isCallAppVisible
     }
 
     override fun addCallback(listener: OngoingCallListener) {
@@ -194,7 +193,7 @@
             }
             updateChipClickListener()
 
-            setUpUidObserver(currentCallNotificationInfo)
+            uidObserver.registerWithUid(currentCallNotificationInfo.uid)
             if (!currentCallNotificationInfo.statusBarSwipedAway) {
                 statusBarWindowController.ifPresent {
                     it.setOngoingProcessRequiresStatusBarVisible(true)
@@ -238,51 +237,6 @@
         }
     }
 
-    /**
-     * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call.
-     */
-    private fun setUpUidObserver(currentCallNotificationInfo: CallNotificationInfo) {
-        isCallAppVisible = isProcessVisibleToUser(
-                iActivityManager.getUidProcessState(currentCallNotificationInfo.uid, null))
-
-        if (uidObserver != null) {
-            iActivityManager.unregisterUidObserver(uidObserver)
-        }
-
-        uidObserver = object : IUidObserver.Stub() {
-            override fun onUidStateChanged(
-                uid: Int,
-                procState: Int,
-                procStateSeq: Long,
-                capability: Int
-            ) {
-                if (uid == currentCallNotificationInfo.uid) {
-                    val oldIsCallAppVisible = isCallAppVisible
-                    isCallAppVisible = isProcessVisibleToUser(procState)
-                    if (oldIsCallAppVisible != isCallAppVisible) {
-                        // Animations may be run as a result of the call's state change, so ensure
-                        // the listener is notified on the main thread.
-                        mainExecutor.execute {
-                            mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
-                        }
-                    }
-                }
-            }
-
-            override fun onUidGone(uid: Int, disabled: Boolean) {}
-            override fun onUidActive(uid: Int) {}
-            override fun onUidIdle(uid: Int, disabled: Boolean) {}
-            override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
-        }
-
-        iActivityManager.registerUidObserver(
-                uidObserver,
-                ActivityManager.UID_OBSERVER_PROCSTATE,
-                ActivityManager.PROCESS_STATE_UNKNOWN,
-                null
-        )
-    }
-
     /** Returns true if the given [procState] represents a process that's visible to the user. */
     private fun isProcessVisibleToUser(procState: Int): Boolean {
         return procState <= ActivityManager.PROCESS_STATE_TOP
@@ -306,9 +260,7 @@
         statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
         swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
         mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
-        if (uidObserver != null) {
-            iActivityManager.unregisterUidObserver(uidObserver)
-        }
+        uidObserver.unregister()
     }
 
     /** Tear down anything related to the chip view to prevent leaks. */
@@ -365,7 +317,84 @@
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         pw.println("Active call notification: $callNotificationInfo")
-        pw.println("Call app visible: $isCallAppVisible")
+        pw.println("Call app visible: ${uidObserver.isCallAppVisible}")
+    }
+
+    /** Our implementation of a [IUidObserver]. */
+    inner class CallAppUidObserver : IUidObserver.Stub() {
+        /** True if the application managing the call is visible to the user. */
+        var isCallAppVisible: Boolean = false
+            private set
+
+        /** The UID of the application managing the call. Null if there is no active call. */
+        private var callAppUid: Int? = null
+
+        /**
+         * True if this observer is currently registered with the activity manager and false
+         * otherwise.
+         */
+        private var isRegistered = false
+
+
+        /** Register this observer with the activity manager and the given [uid]. */
+        fun registerWithUid(uid: Int) {
+            if (callAppUid == uid) {
+                return
+            }
+            callAppUid = uid
+
+            try {
+                isCallAppVisible = isProcessVisibleToUser(
+                    iActivityManager.getUidProcessState(uid, context.opPackageName)
+                )
+                if (isRegistered) {
+                    return
+                }
+                iActivityManager.registerUidObserver(
+                    uidObserver,
+                    ActivityManager.UID_OBSERVER_PROCSTATE,
+                    ActivityManager.PROCESS_STATE_UNKNOWN,
+                    context.opPackageName
+                )
+                isRegistered = true
+            } catch (se: SecurityException) {
+                Log.e(TAG, "Security exception when trying to set up uid observer: $se")
+            }
+        }
+
+        /** Unregister this observer with the activity manager. */
+        fun unregister() {
+            callAppUid = null
+            isRegistered = false
+            iActivityManager.unregisterUidObserver(uidObserver)
+        }
+
+        override fun onUidStateChanged(
+            uid: Int,
+            procState: Int,
+            procStateSeq: Long,
+            capability: Int
+        ) {
+            val currentCallAppUid = callAppUid ?: return
+            if (uid != currentCallAppUid) {
+                return
+            }
+
+            val oldIsCallAppVisible = isCallAppVisible
+            isCallAppVisible = isProcessVisibleToUser(procState)
+            if (oldIsCallAppVisible != isCallAppVisible) {
+                // Animations may be run as a result of the call's state change, so ensure
+                // the listener is notified on the main thread.
+                mainExecutor.execute {
+                    mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+                }
+            }
+        }
+
+        override fun onUidGone(uid: Int, disabled: Boolean) {}
+        override fun onUidActive(uid: Int) {}
+        override fun onUidIdle(uid: Int, disabled: Boolean) {}
+        override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 8471e0a..a32a5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -25,6 +25,7 @@
 import com.android.internal.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.Assert;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -70,6 +71,7 @@
         }
 
         deviceStateManager.registerCallback(executor, state -> {
+            Assert.isMainThread();
             mCurrentDevicePosture =
                     mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
 
@@ -79,11 +81,13 @@
 
     @Override
     public void addCallback(@NonNull Callback listener) {
+        Assert.isMainThread();
         mListeners.add(listener);
     }
 
     @Override
     public void removeCallback(@NonNull Callback listener) {
+        Assert.isMainThread();
         mListeners.remove(listener);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 5e91a25..f52c6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -272,7 +272,7 @@
         }
 
         boolean highPowerOp = areActiveHighPowerLocationRequests();
-        mAreActiveLocationRequests = highPowerOp || shouldDisplay;
+        mAreActiveLocationRequests = shouldDisplay;
         if (mAreActiveLocationRequests != hadActiveLocationRequests) {
             mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 3205e09..48949f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -118,7 +118,6 @@
     private boolean mColorized;
     private int mTint;
     private boolean mResetting;
-    private boolean mWasSpinning;
 
     // TODO(b/193539698): move these to a Controller
     private RemoteInputController mController;
@@ -440,10 +439,6 @@
                 mEditText.requestFocus();
             }
         }
-        if (mWasSpinning) {
-            mController.addSpinning(mEntry.getKey(), mToken);
-            mWasSpinning = false;
-        }
     }
 
     @Override
@@ -452,7 +447,6 @@
         mEditText.removeTextChangedListener(mTextWatcher);
         mEditText.setOnEditorActionListener(null);
         mEditText.mRemoteInputView = null;
-        mWasSpinning = mController.isSpinning(mEntry.getKey(), mToken);
         if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
             return;
         }
@@ -539,8 +533,6 @@
         if (isActive() && mWrapper != null) {
             mWrapper.setRemoteInputVisible(true);
         }
-
-        mWasSpinning = false;
     }
 
     private void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 2e627a8..e2374ad 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.unfold
 
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
 import android.os.Handler
 import android.os.PowerManager
 import android.provider.Settings
+import androidx.core.view.OneShotPreDrawListener
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.statusbar.LightRevealScrim
@@ -27,6 +30,8 @@
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
 import com.android.systemui.util.settings.GlobalSettings
+import java.util.concurrent.Executor
+import java.util.function.Consumer
 import javax.inject.Inject
 
 /**
@@ -38,30 +43,39 @@
 @Inject
 constructor(
     @Main private val handler: Handler,
+    @Main private val executor: Executor,
+    private val context: Context,
+    private val deviceStateManager: DeviceStateManager,
     private val wakefulnessLifecycle: WakefulnessLifecycle,
     private val globalSettings: GlobalSettings
 ) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
 
-    private var alwaysOnEnabled: Boolean = false
-    private var isScrimOpaque: Boolean = false
     private lateinit var statusBar: StatusBar
+
+    private var isFolded = false
+    private var isFoldHandled = true
+
+    private var alwaysOnEnabled: Boolean = false
+    private var isDozing: Boolean = false
+    private var isScrimOpaque: Boolean = false
     private var pendingScrimReadyCallback: Runnable? = null
 
     private var shouldPlayAnimation = false
+    private var isAnimationPlaying = false
+
     private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
 
     private val startAnimationRunnable = Runnable {
         statusBar.notificationPanelViewController.startFoldToAodAnimation {
             // End action
-            isAnimationPlaying = false
+            setAnimationState(playing = false)
         }
     }
 
-    private var isAnimationPlaying = false
-
     override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
         this.statusBar = statusBar
 
+        deviceStateManager.registerCallback(executor, FoldListener())
         wakefulnessLifecycle.addObserver(this)
     }
 
@@ -71,28 +85,29 @@
     override fun startAnimation(): Boolean =
         if (alwaysOnEnabled &&
             wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
-            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0") {
-            shouldPlayAnimation = true
-
-            isAnimationPlaying = true
+            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
+        ) {
+            setAnimationState(playing = true)
             statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
-
-            statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
-
             true
         } else {
-            shouldPlayAnimation = false
+            setAnimationState(playing = false)
             false
         }
 
     override fun onStartedWakingUp() {
         if (isAnimationPlaying) {
             handler.removeCallbacks(startAnimationRunnable)
-            statusBar.notificationPanelViewController.cancelFoldToAodAnimation();
+            statusBar.notificationPanelViewController.cancelFoldToAodAnimation()
         }
 
-        shouldPlayAnimation = false
-        isAnimationPlaying = false
+        setAnimationState(playing = false)
+    }
+
+    private fun setAnimationState(playing: Boolean) {
+        shouldPlayAnimation = playing
+        isAnimationPlaying = playing
+        statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
     }
 
     /**
@@ -104,11 +119,24 @@
      */
     fun onScreenTurningOn(onReady: Runnable) {
         if (shouldPlayAnimation) {
+            // The device was not dozing and going to sleep after folding, play the animation
+
             if (isScrimOpaque) {
                 onReady.run()
             } else {
                 pendingScrimReadyCallback = onReady
             }
+        } else if (isFolded && !isFoldHandled && alwaysOnEnabled && isDozing) {
+            // Screen turning on for the first time after folding and we are already dozing
+            // We should play the folding to AOD animation
+
+            setAnimationState(playing = true)
+            statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+            // We don't need to wait for the scrim as it is already displayed
+            // but we should wait for the initial animation preparations to be drawn
+            // (setting initial alpha/translation)
+            OneShotPreDrawListener.add(statusBar.notificationPanelViewController.view, onReady)
         } else {
             // No animation, call ready callback immediately
             onReady.run()
@@ -135,6 +163,10 @@
         }
     }
 
+    fun setIsDozing(dozing: Boolean) {
+        isDozing = dozing
+    }
+
     override fun isAnimationPlaying(): Boolean = isAnimationPlaying
 
     override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -165,4 +197,15 @@
     interface FoldAodAnimationStatus {
         fun onFoldToAodAnimationChanged()
     }
+
+    private inner class FoldListener :
+        DeviceStateManager.FoldStateListener(
+            context,
+            Consumer { isFolded ->
+                if (!isFolded) {
+                    // We are unfolded now, reset the fold handle status
+                    isFoldHandled = false
+                }
+                this.isFolded = isFolded
+            })
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
index 3a270bb..0686071c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
@@ -19,6 +19,7 @@
 import android.app.Dialog;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.UserManager;
 
@@ -70,10 +71,12 @@
             }
 
             Drawable newUserIcon = userIcon;
+            Resources res = mContext.getResources();
             if (newUserIcon == null) {
-                newUserIcon = UserIcons.getDefaultUserIcon(mContext.getResources(), user.id, false);
+                newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
             }
-            mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
+            mUserManager.setUserIcon(
+                    user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));
 
             userCreationProgressDialog.dismiss();
             successCallback.accept(user);
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 14585fb..c0d7925 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -35,9 +35,8 @@
 import android.widget.ArrayAdapter
 import android.widget.ImageView
 import android.widget.TextView
-
 import androidx.constraintlayout.helper.widget.Flow
-
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.util.UserIcons
 import com.android.settingslib.Utils
 import com.android.systemui.R
@@ -47,12 +46,12 @@
 import com.android.systemui.statusbar.phone.ShadeController
 import com.android.systemui.statusbar.policy.UserSwitcherController
 import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
 import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
 import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
 import com.android.systemui.util.LifecycleActivity
-
 import javax.inject.Inject
+import kotlin.math.ceil
 
 private const val USER_VIEW = "user_view"
 
@@ -137,6 +136,18 @@
             return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
         }
 
+        fun getTotalUserViews(): Int {
+            return users.count { item ->
+                !doNotRenderUserView(item)
+            }
+        }
+
+        fun doNotRenderUserView(item: UserRecord): Boolean {
+            return item.isAddUser ||
+                    item.isAddSupervisedUser ||
+                    item.isGuest && item.info == null
+        }
+
         private fun getDrawable(item: UserRecord): Drawable {
             var drawable = if (item.isCurrent && item.isGuest) {
                 getDrawable(R.drawable.ic_avatar_guest_user)
@@ -211,7 +222,8 @@
 
         userSwitcherController.init(parent)
         initBroadcastReceiver()
-        buildUserViews()
+
+        parent.post { buildUserViews() }
     }
 
     private fun showPopupMenu() {
@@ -272,16 +284,32 @@
         }
         parent.removeViews(start, count)
         addUserRecords.clear()
-
         val flow = requireViewById<Flow>(R.id.flow)
+        val totalWidth = parent.width
+        val userViewCount = adapter.getTotalUserViews()
+        val maxColumns = getMaxColumns(userViewCount)
+        val horizontalGap = resources
+            .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
+        val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
+        val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
+
+        flow.setMaxElementsWrap(maxColumns)
+
         for (i in 0 until adapter.getCount()) {
             val item = adapter.getItem(i)
-            if (item.isAddUser ||
-                item.isAddSupervisedUser ||
-                item.isGuest && item.info == null) {
+            if (adapter.doNotRenderUserView(item)) {
                 addUserRecords.add(item)
             } else {
                 val userView = adapter.getView(i, null, parent)
+                userView.requireViewById<ImageView>(R.id.user_switcher_icon).apply {
+                    val lp = layoutParams
+                    if (maxWidgetDiameter < lp.width) {
+                        lp.width = maxWidgetDiameter
+                        lp.height = maxWidgetDiameter
+                        layoutParams = lp
+                    }
+                }
+
                 userView.setId(View.generateViewId())
                 parent.addView(userView)
 
@@ -333,6 +361,11 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
     }
 
+    @VisibleForTesting
+    fun getMaxColumns(userCount: Int): Int {
+        return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
+    }
+
     private class ItemAdapter(
         val parentContext: Context,
         val resource: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
index 40982bb..460b7d9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
@@ -37,6 +37,8 @@
     private final ThresholdSensor[] mPostureToPrimaryProxSensorMap;
     private final ThresholdSensor[] mPostureToSecondaryProxSensorMap;
 
+    private final DevicePostureController mDevicePostureController;
+
     @Inject
     PostureDependentProximitySensor(
             @PrimaryProxSensor ThresholdSensor[] postureToPrimaryProxSensorMap,
@@ -53,15 +55,24 @@
         );
         mPostureToPrimaryProxSensorMap = postureToPrimaryProxSensorMap;
         mPostureToSecondaryProxSensorMap = postureToSecondaryProxSensorMap;
-        mDevicePosture = devicePostureController.getDevicePosture();
-        devicePostureController.addCallback(mDevicePostureCallback);
+        mDevicePostureController = devicePostureController;
+
+        mDevicePosture = mDevicePostureController.getDevicePosture();
+        mDevicePostureController.addCallback(mDevicePostureCallback);
 
         chooseSensors();
     }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+        mDevicePostureController.removeCallback(mDevicePostureCallback);
+    }
+
     private void chooseSensors() {
         if (mDevicePosture >= mPostureToPrimaryProxSensorMap.length
                 || mDevicePosture >= mPostureToSecondaryProxSensorMap.length) {
-            Log.e("PostureDependentProxSensor",
+            Log.e("PostureDependProxSensor",
                     "unsupported devicePosture=" + mDevicePosture);
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
index a8a6341..c06a3a1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
@@ -73,6 +73,13 @@
         }
     }
 
+    /**
+     * Cleanup after no longer needed.
+     */
+    public void destroy() {
+        mSensor.destroy();
+    }
+
     private void unregister() {
         mSensor.unregister(mListener);
         mRegistered.set(false);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index d3f1c93..7f64322 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -42,4 +42,10 @@
      * of what is reported by the primary sensor.
      */
     void setSecondarySafe(boolean safe);
+
+    /**
+     * Called when the proximity sensor is no longer needed. All listeners should
+     * be unregistered and cleaned up.
+     */
+    void destroy();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
index 5568f64..8ab5bc6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
@@ -252,6 +252,11 @@
     }
 
     @Override
+    public void destroy() {
+        pause();
+    }
+
+    @Override
     public String getName() {
         return mPrimaryThresholdSensor.getName();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 5e9bae9..d2142dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -48,8 +48,8 @@
                         R.dimen.card_carousel_dot_selected_radius);
         mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
 
-        mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
-        mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
+        mUnselectedColor = context.getColor(R.color.material_dynamic_neutral70);
+        mSelectedColor = context.getColor(R.color.material_dynamic_neutral100);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 9917777..92e7f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -278,7 +278,7 @@
 
     private Drawable getHomeIndicatorDrawable() {
         Drawable drawable = getDrawable(R.drawable.ic_close);
-        drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+        drawable.setTint(getColor(R.color.material_dynamic_neutral70));
         return drawable;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 6fefce2..b2a79b0 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -21,7 +21,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -97,7 +97,7 @@
         implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
     private static final String TAG = WMShell.class.getName();
     private static final int INVALID_SYSUI_STATE_MASK =
-            SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
+            SYSUI_STATE_DIALOG_SHOWING
                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
                     | SYSUI_STATE_BOUNCER_SHOWING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 40632a8..7a0db1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -42,6 +42,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -112,6 +113,11 @@
         // KeyguardUpdateMonitor to be created (injected).
         // TODO(b/1531701009) Clean up NotificationContentView creation to prevent this
         mDependency.injectMockDependency(SmartReplyController.class);
+
+        // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+        // is missing (constructing the actual one would throw).
+        // TODO(b/219008720): Remove this.
+        mDependency.injectMockDependency(SystemUIDialogManager.class);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 1dd5e22..6e5926d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -88,6 +88,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
 @TestableLooper.RunWithLooper
@@ -345,15 +346,17 @@
 
     @Test
     public void onOrientationChanged_disabled_updateDisplayRotation() {
-        final Display display = Mockito.spy(mContext.getDisplay());
-        when(display.getRotation()).thenReturn(Surface.ROTATION_90);
-        when(mContext.getDisplay()).thenReturn(display);
+        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+        // Rotate the window clockwise 90 degree.
+        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+                windowBounds.right);
+        mWindowManager.setWindowBounds(windowBounds);
+        final int newRotation = simulateRotateTheDevice();
 
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
-        });
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
+                ActivityInfo.CONFIG_ORIENTATION));
 
-        assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
     }
 
     @Test
@@ -603,6 +606,113 @@
         ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
     }
 
+    @Test
+    public void setMinimumWindowSize_enabled_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                    Float.NaN, Float.NaN);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
+                    minimumWindowSize - 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(minimumWindowSize, actualWindowHeight.get());
+        assertEquals(minimumWindowSize, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(bounds.height(), actualWindowHeight.get());
+        assertEquals(bounds.width(), actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
+
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger magnificationCenterX = new AtomicInteger();
+        final AtomicInteger magnificationCenterY = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
+                    minimumWindowSize, bounds.right, bounds.bottom);
+            magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
+            magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
+        });
+
+        assertTrue(magnificationCenterX.get() < bounds.right);
+        assertTrue(magnificationCenterY.get() < bounds.bottom);
+    }
+
     private CharSequence getAccessibilityWindowTitle() {
         final View mirrorView = mWindowManager.getAttachedView();
         if (mirrorView == null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 589eeb5..d5df9fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,7 +46,7 @@
 @RunWithLooper
 class ActivityLaunchAnimatorTest : SysuiTestCase() {
     private val launchContainer = LinearLayout(mContext)
-    private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+    private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
     @Mock lateinit var callback: ActivityLaunchAnimator.Callback
     @Mock lateinit var listener: ActivityLaunchAnimator.Listener
     @Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -58,7 +58,7 @@
 
     @Before
     fun setup() {
-        activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
+        activityLaunchAnimator = ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator)
         activityLaunchAnimator.callback = callback
         activityLaunchAnimator.addListener(listener)
     }
@@ -129,13 +129,11 @@
     @Test
     fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
         `when`(callback.isOnKeyguard()).thenReturn(true)
-        val animator = ActivityLaunchAnimator(launchAnimator)
-        animator.callback = callback
 
         val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
         var animationAdapter: RemoteAnimationAdapter? = null
 
-        startIntentWithAnimation(animator) { adapter ->
+        startIntentWithAnimation(activityLaunchAnimator) { adapter ->
             animationAdapter = adapter
             ActivityManager.START_DELIVERED_TO_TOP
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 61e78f5..fe2efa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -20,8 +20,10 @@
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
 import junit.framework.Assert.assertTrue
 import org.junit.After
+import org.junit.Assert.assertNotEquals
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -43,7 +45,7 @@
     @Before
     fun setUp() {
         dialogLaunchAnimator = DialogLaunchAnimator(
-            dreamManager, launchAnimator, forceDisableSynchronization = true)
+            dreamManager, launchAnimator, isForTesting = true)
     }
 
     @After
@@ -92,11 +94,6 @@
 
         // Clicking the transparent background should dismiss the dialog.
         runOnMainThreadAndWaitForIdleSync {
-            // TODO(b/204561691): Remove this call to disableAllCurrentDialogsExitAnimations() and
-            // make sure that the test still pass on git_master/cf_x86_64_phone-userdebug in
-            // Forrest.
-            dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
-
             transparentBackground.performClick()
         }
         assertFalse(dialog.isShowing)
@@ -110,7 +107,6 @@
         assertTrue(firstDialog.isShowing)
         assertTrue(secondDialog.isShowing)
         runOnMainThreadAndWaitForIdleSync {
-            dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
             dialogLaunchAnimator.dismissStack(secondDialog)
         }
 
@@ -118,7 +114,63 @@
         assertFalse(secondDialog.isShowing)
     }
 
+    @Test
+    fun testActivityLaunchControllerFromDialog() {
+        val firstDialog = createAndShowDialog()
+        val secondDialog = createDialogAndShowFromDialog(firstDialog)
+
+        val controller =
+            dialogLaunchAnimator.createActivityLaunchController(secondDialog.contentView)!!
+
+        // The dialog shouldn't be dismissable during the animation.
+        runOnMainThreadAndWaitForIdleSync {
+            controller.onLaunchAnimationStart(isExpandingFullyAbove = true)
+            secondDialog.dismiss()
+        }
+        assertTrue(secondDialog.isShowing)
+
+        // Both dialogs should be dismissed at the end of the animation.
+        runOnMainThreadAndWaitForIdleSync {
+            controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
+        }
+        assertFalse(firstDialog.isShowing)
+        assertFalse(secondDialog.isShowing)
+    }
+
+    @Test
+    fun testActivityLaunchFromHiddenDialog() {
+        val dialog = createAndShowDialog()
+        runOnMainThreadAndWaitForIdleSync {
+            dialog.hide()
+        }
+        assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+    }
+
+    @Test
+    fun testDialogAnimationIsChangedByAnimator() {
+        // Important: the power menu animation relies on this behavior to know when to animate (see
+        // http://ag/16774605).
+        val dialog = runOnMainThreadAndWaitForIdleSync { TestDialog(context) }
+        dialog.window.setWindowAnimations(0)
+        assertEquals(0, dialog.window.attributes.windowAnimations)
+
+        val touchSurface = createTouchSurface()
+        runOnMainThreadAndWaitForIdleSync {
+            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+        }
+        assertNotEquals(0, dialog.window.attributes.windowAnimations)
+    }
+
     private fun createAndShowDialog(): TestDialog {
+        val touchSurface = createTouchSurface()
+        return runOnMainThreadAndWaitForIdleSync {
+            val dialog = TestDialog(context)
+            dialogLaunchAnimator.showFromView(dialog, touchSurface)
+            dialog
+        }
+    }
+
+    private fun createTouchSurface(): View {
         return runOnMainThreadAndWaitForIdleSync {
             val touchSurfaceRoot = LinearLayout(context)
             val touchSurface = View(context)
@@ -129,9 +181,7 @@
             ViewUtils.attachView(touchSurfaceRoot)
             attachedViews.add(touchSurfaceRoot)
 
-            val dialog = TestDialog(context)
-            dialogLaunchAnimator.showFromView(dialog, touchSurface)
-            dialog
+            touchSurface
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
new file mode 100644
index 0000000..e15b6cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.clipboardoverlay;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.DeviceConfigProxyFake;
+
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ClipboardListenerTest extends SysuiTestCase {
+
+    @Mock
+    private ClipboardManager mClipboardManager;
+    @Mock
+    private ClipboardOverlayControllerFactory mClipboardOverlayControllerFactory;
+    @Mock
+    private ClipboardOverlayController mOverlayController;
+    private DeviceConfigProxyFake mDeviceConfigProxy;
+
+    private ClipData mSampleClipData;
+    private String mSampleSource = "Example source";
+
+    @Captor
+    private ArgumentCaptor<Runnable> mRunnableCaptor;
+    @Captor
+    private ArgumentCaptor<ClipData> mClipDataCaptor;
+    @Captor
+    private ArgumentCaptor<String> mStringCaptor;
+
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mClipboardOverlayControllerFactory.create(any())).thenReturn(
+                mOverlayController);
+        when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
+
+
+        mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
+                new ClipData.Item("Test Item"));
+        when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
+        when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
+
+        mDeviceConfigProxy = new DeviceConfigProxyFake();
+    }
+
+    @Test
+    public void test_disabled() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "false", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        verifyZeroInteractions(mClipboardManager);
+    }
+
+    @Test
+    public void test_enabled() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "true", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        verify(mClipboardManager).addPrimaryClipChangedListener(any());
+    }
+
+    @Test
+    public void test_consecutiveCopies() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "true", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory).create(any());
+
+        verify(mOverlayController).setClipData(mClipDataCaptor.capture(), mStringCaptor.capture());
+
+        assertEquals(mSampleClipData, mClipDataCaptor.getValue());
+        assertEquals(mSampleSource, mStringCaptor.getValue());
+
+        verify(mOverlayController).setOnSessionCompleteListener(mRunnableCaptor.capture());
+
+        // Should clear the overlay controller
+        mRunnableCaptor.getValue().run();
+
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+
+        // Not calling the runnable here, just change the clip again and verify that the overlay is
+        // NOT recreated.
+
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
deleted file mode 100644
index 500205c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.wifi.WifiInfo;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-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.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class CommunalTrustedNetworkConditionTest extends SysuiTestCase {
-    @Mock private ConnectivityManager mConnectivityManager;
-
-    @Captor private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor;
-
-    private final Handler mHandler = new FakeHandler(Looper.getMainLooper());
-    private CommunalTrustedNetworkCondition mCondition;
-
-    private final String mTrustedWifi1 = "wifi-1";
-    private final String mTrustedWifi2 = "wifi-2";
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        final FakeSettings secureSettings = new FakeSettings();
-        secureSettings.putStringForUser(Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS,
-                mTrustedWifi1 + CommunalTrustedNetworkCondition.SETTINGS_STRING_DELIMINATOR
-                        + mTrustedWifi2, UserHandle.USER_SYSTEM);
-        mCondition = new CommunalTrustedNetworkCondition(mHandler, mConnectivityManager,
-                secureSettings);
-    }
-
-    @Test
-    public void updateCallback_connectedToTrustedNetwork_reportsTrue() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void updateCallback_switchedToAnotherTrustedNetwork_reportsNothing() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to a trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-        clearInvocations(callback);
-
-        // Connected to another trusted Wi-Fi network.
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
-
-        // Verifies that the callback is not triggered.
-        verify(callback, never()).onConditionChanged(eq(mCondition));
-    }
-
-    @Test
-    public void updateCallback_connectedToNonTrustedNetwork_reportsFalse() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to trusted Wi-Fi network.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-
-        Mockito.clearInvocations(callback);
-        // Connected to non-trusted Wi-Fi network.
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void updateCallback_disconnectedFromNetwork_reportsFalse() {
-        final CommunalTrustedNetworkCondition.Callback callback =
-                mock(CommunalTrustedNetworkCondition.Callback.class);
-        mCondition.addCallback(callback);
-
-        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
-
-        // Connected to Wi-Fi.
-        final Network network = mock(Network.class);
-        networkCallback.onAvailable(network);
-        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
-        clearInvocations(callback);
-
-        // Disconnected from Wi-Fi.
-        networkCallback.onLost(network);
-
-        // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    // Captures and returns the network callback, assuming it is registered with the connectivity
-    // manager.
-    private ConnectivityManager.NetworkCallback captureNetworkCallback() {
-        verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
-                mNetworkCallbackCaptor.capture());
-        return mNetworkCallbackCaptor.getValue();
-    }
-
-    private NetworkCapabilities fakeNetworkCapabilities(String ssid) {
-        final NetworkCapabilities networkCapabilities = mock(NetworkCapabilities.class);
-        final WifiInfo wifiInfo = mock(WifiInfo.class);
-        when(wifiInfo.getSSID()).thenReturn(ssid);
-        when(networkCapabilities.getTransportInfo()).thenReturn(wifiInfo);
-        return networkCapabilities;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 47ab17d..d3c465d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -23,22 +23,24 @@
 import android.graphics.drawable.Icon
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
+import android.service.controls.templates.ControlTemplate
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
+import android.view.View
 import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.runner.RunWith
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsMetricsLogger
 import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
 
 @SmallTest
@@ -49,11 +51,12 @@
     private val clock = FakeSystemClock()
 
     private lateinit var cvh: ControlViewHolder
+    private lateinit var baseLayout: ViewGroup
 
     @Before
     fun setUp() {
         TestableLooper.get(this).runWithLooper {
-            val baseLayout = LayoutInflater.from(mContext).inflate(
+            baseLayout = LayoutInflater.from(mContext).inflate(
                     R.layout.controls_base_item, null, false) as ViewGroup
 
             cvh = ControlViewHolder(
@@ -106,6 +109,25 @@
 
         assertThat(cvh.icon.imageTintList).isEqualTo(customIconTintList)
     }
+
+    @Test
+    fun chevronIcon() {
+        val control = Control.StatefulBuilder(CONTROL_ID, mock(PendingIntent::class.java))
+            .setStatus(Control.STATUS_OK)
+            .setControlTemplate(ControlTemplate.NO_TEMPLATE)
+            .build()
+        val cws = ControlWithState(
+            ComponentName.createRelative("pkg", "cls"),
+            ControlInfo(
+                CONTROL_ID, CONTROL_TITLE, "subtitle", DeviceTypes.TYPE_AIR_FRESHENER
+            ),
+            control
+        )
+        cvh.bindData(cws, false)
+        val chevronIcon = baseLayout.findViewById<View>(R.id.chevron_icon)
+
+        assertThat(chevronIcon.visibility).isEqualTo(View.VISIBLE)
+    }
 }
 
 private const val CONTROL_ID = "CONTROL_ID"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 8078b6c..5684429 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -42,12 +42,14 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.testing.AndroidTestingRunner;
 import android.testing.UiThreadTest;
+import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
@@ -444,4 +446,20 @@
 
         assertTrue(mServiceFake.requestedWakeup);
     }
+
+    @Test
+    public void testDozePulsing_displayRequiresBlanking_screenState() {
+        DozeParameters dozeParameters = mock(DozeParameters.class);
+        when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
+
+        assertEquals(Display.STATE_OFF, DOZE_REQUEST_PULSE.screenState(dozeParameters));
+    }
+
+    @Test
+    public void testDozePulsing_displayDoesNotRequireBlanking_screenState() {
+        DozeParameters dozeParameters = mock(DozeParameters.class);
+        when(dozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+
+        assertEquals(Display.STATE_ON, DOZE_REQUEST_PULSE.screenState(dozeParameters));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index f207b9e..0a1e45c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -175,6 +175,7 @@
     public void testDestroy() {
         mDozeSensors.destroy();
 
+        verify(mProximitySensor).destroy();
         verify(mTriggerSensor).setListening(false);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 7fc354f..ae387e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -299,6 +299,12 @@
         verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor));
     }
 
+    @Test
+    public void testDestroy() {
+        mTriggers.destroy();
+        verify(mProximityCheck).destroy();
+    }
+
     private void waitForSensorManager() {
         mExecutor.runAllReady();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 627da3c..515a1ac8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -123,7 +123,7 @@
     }
 
     @Test
-    public void testComplicationFiltering() {
+    public void testComplicationFilteringWhenShouldShowComplications() {
         final DreamOverlayStateController stateController =
                 new DreamOverlayStateController(mExecutor);
 
@@ -160,4 +160,50 @@
         }
 
     }
+
+    @Test
+    public void testComplicationFilteringWhenShouldHideComplications() {
+        final DreamOverlayStateController stateController =
+                new DreamOverlayStateController(mExecutor);
+        stateController.setShouldShowComplications(true);
+
+        final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
+        final Complication weatherComplication = Mockito.mock(Complication.class);
+        when(alwaysAvailableComplication.getRequiredTypeAvailability())
+                .thenReturn(Complication.COMPLICATION_TYPE_NONE);
+        when(weatherComplication.getRequiredTypeAvailability())
+                .thenReturn(Complication.COMPLICATION_TYPE_WEATHER);
+
+        stateController.addComplication(alwaysAvailableComplication);
+        stateController.addComplication(weatherComplication);
+
+        final DreamOverlayStateController.Callback callback =
+                Mockito.mock(DreamOverlayStateController.Callback.class);
+
+        stateController.setAvailableComplicationTypes(Complication.COMPLICATION_TYPE_WEATHER);
+        stateController.addCallback(callback);
+        mExecutor.runAllReady();
+
+        {
+            clearInvocations(callback);
+            stateController.setShouldShowComplications(true);
+            mExecutor.runAllReady();
+
+            verify(callback).onAvailableComplicationTypesChanged();
+            final Collection<Complication> complications = stateController.getComplications();
+            assertThat(complications.contains(alwaysAvailableComplication)).isTrue();
+            assertThat(complications.contains(weatherComplication)).isTrue();
+        }
+
+        {
+            clearInvocations(callback);
+            stateController.setShouldShowComplications(false);
+            mExecutor.runAllReady();
+
+            verify(callback).onAvailableComplicationTypesChanged();
+            final Collection<Complication> complications = stateController.getComplications();
+            assertThat(complications.contains(alwaysAvailableComplication)).isTrue();
+            assertThat(complications.contains(weatherComplication)).isFalse();
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 7f72dda..6587029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -29,8 +29,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.statusbar.policy.BatteryController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,10 +44,6 @@
     @Mock
     DreamOverlayStatusBarView mView;
     @Mock
-    BatteryController mBatteryController;
-    @Mock
-    BatteryMeterViewController mBatteryMeterViewController;
-    @Mock
     ConnectivityManager mConnectivityManager;
     @Mock
     NetworkCapabilities mNetworkCapabilities;
@@ -61,22 +55,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mController = new DreamOverlayStatusBarViewController(
-                mContext, mView, mBatteryController, mBatteryMeterViewController,
-                mConnectivityManager);
-    }
-
-    @Test
-    public void testOnInitInitializesControllers() {
-        mController.onInit();
-        verify(mBatteryMeterViewController).init();
-    }
-
-    @Test
-    public void testOnViewAttachedAddsBatteryControllerCallback() {
-        mController.onViewAttached();
-        verify(mBatteryController)
-                .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+        mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager);
     }
 
     @Test
@@ -113,13 +92,6 @@
     }
 
     @Test
-    public void testOnViewDetachedRemovesBatteryControllerCallback() {
-        mController.onViewDetached();
-        verify(mBatteryController)
-                .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
-    }
-
-    @Test
     public void testOnViewDetachedUnregistersNetworkCallback() {
         mController.onViewDetached();
         verify(mConnectivityManager)
@@ -127,26 +99,6 @@
     }
 
     @Test
-    public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
-        final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
-                ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
-        mController.onViewAttached();
-        verify(mBatteryController).addCallback(callbackCapture.capture());
-        callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
-        verify(mView).showBatteryPercentText(true);
-    }
-
-    @Test
-    public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
-        final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
-                ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
-        mController.onViewAttached();
-        verify(mBatteryController).addCallback(callbackCapture.capture());
-        callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
-        verify(mView).showBatteryPercentText(false);
-    }
-
-    @Test
     public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
         // Make sure wifi starts out unavailable when onViewAttached is called.
         when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
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 71fc8ee..953be7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -19,7 +19,6 @@
 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.eq;
 import static org.mockito.Mockito.doReturn;
@@ -57,13 +56,11 @@
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.model.SysUiState;
 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;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -112,7 +109,6 @@
     @Mock private UiEventLogger mUiEventLogger;
     @Mock private RingerModeTracker mRingerModeTracker;
     @Mock private RingerModeLiveData mRingerModeLiveData;
-    @Mock private SysUiState mSysUiState;
     @Mock private PackageManager mPackageManager;
     @Mock private Handler mHandler;
     @Mock private UserContextProvider mUserContextProvider;
@@ -120,7 +116,6 @@
     @Mock private StatusBar mStatusBar;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
-    @Mock private SystemUIDialogManager mDialogManager;
 
     private TestableLooper mTestableLooper;
 
@@ -161,19 +156,16 @@
                 mBackgroundExecutor,
                 mUiEventLogger,
                 mRingerModeTracker,
-                mSysUiState,
                 mHandler,
                 mPackageManager,
                 Optional.of(mStatusBar),
                 mKeyguardUpdateMonitor,
-                mDialogLaunchAnimator,
-                mDialogManager);
+                mDialogLaunchAnimator);
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
         ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
         backdropColors.setMainColor(Color.BLACK);
         when(mColorExtractor.getNeutralColors()).thenReturn(backdropColors);
-        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
deleted file mode 100644
index 98b9252..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle
-
-import android.hardware.Sensor
-import android.hardware.SensorEventListener
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.sensors.AsyncSensorManager
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.any
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.never
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class AmbientLightModeMonitorTest : SysuiTestCase() {
-    @Mock private lateinit var sensorManager: AsyncSensorManager
-    @Mock private lateinit var sensor: Sensor
-    @Mock private lateinit var algorithm: AmbientLightModeMonitor.DebounceAlgorithm
-
-    private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor)
-
-        ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
-    }
-
-    @Test
-    fun shouldRegisterSensorEventListenerOnStart() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(sensorManager).registerListener(any(), eq(sensor), anyInt())
-    }
-
-    @Test
-    fun shouldUnregisterSensorEventListenerOnStop() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        val sensorEventListener = captureSensorEventListener()
-
-        ambientLightModeMonitor.stop()
-
-        verify(sensorManager).unregisterListener(eq(sensorEventListener))
-    }
-
-    @Test
-    fun shouldStartDebounceAlgorithmOnStart() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(algorithm).start(eq(callback))
-    }
-
-    @Test
-    fun shouldStopDebounceAlgorithmOnStop() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-        ambientLightModeMonitor.stop()
-
-        verify(algorithm).stop()
-    }
-
-    @Test
-    fun shouldNotRegisterForSensorUpdatesIfSensorNotAvailable() {
-        `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(null)
-        val ambientLightModeMonitor = AmbientLightModeMonitor(algorithm, sensorManager)
-
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        ambientLightModeMonitor.start(callback)
-
-        verify(sensorManager, never()).registerListener(any(), any(Sensor::class.java), anyInt())
-    }
-
-    // Captures [SensorEventListener], assuming it has been registered with [sensorManager].
-    private fun captureSensorEventListener(): SensorEventListener {
-        val captor = ArgumentCaptor.forClass(SensorEventListener::class.java)
-        verify(sensorManager).registerListener(captor.capture(), any(), anyInt())
-        return captor.value
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
deleted file mode 100644
index 3c24a3a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.os.PowerManager;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import javax.inject.Provider;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class IdleHostViewControllerTest extends SysuiTestCase {
-    @Mock private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock private PowerManager mPowerManager;
-    @Mock private IdleHostView mIdleHostView;
-    @Mock private Resources mResources;
-    @Mock private Provider<View> mViewProvider;
-    @Mock private KeyguardStateController mKeyguardStateController;
-    @Mock private StatusBarStateController mStatusBarStateController;
-    @Mock private AmbientLightModeMonitor mAmbientLightModeMonitor;
-
-    private KeyguardStateController.Callback mKeyguardStateCallback;
-    private StatusBarStateController.StateListener mStatusBarStateListener;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-
-        final IdleHostViewController controller = new IdleHostViewController(mBroadcastDispatcher,
-                mPowerManager, mIdleHostView, mResources, mViewProvider, mKeyguardStateController,
-                mStatusBarStateController, mAmbientLightModeMonitor);
-        controller.init();
-        controller.onViewAttached();
-
-        // Captures keyguard state controller callback.
-        ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
-                ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
-        verify(mKeyguardStateController).addCallback(keyguardStateCallbackCaptor.capture());
-        mKeyguardStateCallback = keyguardStateCallbackCaptor.getValue();
-
-        // Captures status bar state listener.
-        ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
-                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-        verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
-        mStatusBarStateListener = statusBarStateListenerCaptor.getValue();
-    }
-
-    @Test
-    public void testStartDozingWhenLowLight() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // Regular ambient lighting.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Verifies it doesn't go to sleep yet.
-        verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
-
-        // Ambient lighting becomes dim.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
-        // Verifies it goes to sleep.
-        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-    }
-
-    @Test
-    public void testWakeUpWhenRegularLight() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // In low light / dozing.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
-
-        // Regular ambient lighting.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Verifies it wakes up from sleep.
-        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
-    }
-
-    // Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor
-    // has been started.
-    private AmbientLightModeMonitor.Callback captureAmbientLightModeMonitorCallback() {
-        ArgumentCaptor<AmbientLightModeMonitor.Callback> captor =
-                ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
-        verify(mAmbientLightModeMonitor).start(captor.capture());
-        return captor.getValue();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
deleted file mode 100644
index ebc7345..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/LightSensorDebounceAlgorithmTest.kt
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.idle
-
-import android.content.res.Resources
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.`when`
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class LightSensorDebounceAlgorithmTest : SysuiTestCase() {
-    @Mock private lateinit var resources: Resources
-
-    private val systemClock = FakeSystemClock()
-    private val executor = FakeExecutor(systemClock)
-
-    private lateinit var algorithm: LightSensorEventsDebounceAlgorithm
-
-    private val mockLightModeThreshold = 5
-    private val mockDarkModeThreshold = 2
-    private val mockLightModeSpan = 100
-    private val mockDarkModeSpan = 50
-    private val mockLightModeFrequency = 10
-    private val mockDarkModeFrequency = 5
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(resources.getInteger(R.integer.config_ambientLightModeThreshold))
-                .thenReturn(mockLightModeThreshold)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeThreshold))
-                .thenReturn(mockDarkModeThreshold)
-        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingSpanMillis))
-                .thenReturn(mockLightModeSpan)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingSpanMillis))
-                .thenReturn(mockDarkModeSpan)
-        `when`(resources.getInteger(R.integer.config_ambientLightModeSamplingFrequencyMillis))
-                .thenReturn(mockLightModeFrequency)
-        `when`(resources.getInteger(R.integer.config_ambientDarkModeSamplingFrequencyMillis))
-                .thenReturn(mockDarkModeFrequency)
-
-        algorithm = LightSensorEventsDebounceAlgorithm(executor, resources)
-    }
-
-    @Test
-    fun shouldOnlyTriggerCallbackWhenValueChanges() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-
-        // Light mode, should trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-        reset(callback)
-
-        // Light mode again, should NOT trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-        reset(callback)
-
-        // Dark mode, should trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-        reset(callback)
-
-        // Dark mode again, should not trigger callback.
-        algorithm.mode = AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
-        verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    @Test
-    fun shouldReportUndecidedWhenNeitherLightNorDarkClaimIsTrue() {
-        algorithm.isDarkMode = false
-        algorithm.isLightMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
-    }
-
-    @Test
-    fun shouldReportDarkModeAsLongAsDarkModeClaimIsTrue() {
-        algorithm.isDarkMode = true
-        algorithm.isLightMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-
-        algorithm.isLightMode = true
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    @Test
-    fun shouldReportLightModeWhenLightModeClaimIsTrueAndDarkModeClaimIsFalse() {
-        algorithm.isLightMode = true
-        algorithm.isDarkMode = false
-
-        assertThat(algorithm.mode).isEqualTo(
-                AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-    }
-
-    @Test
-    fun shouldSetIsLightModeToTrueWhenBundleAverageIsGreaterThanThreshold() {
-        // Note: [mockLightModeThreshold] is 5.0.
-        algorithm.bundleAverageLightMode = 5.1
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 10.0
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 20.0
-        assertThat(algorithm.isLightMode).isTrue()
-
-        algorithm.bundleAverageLightMode = 5.0
-        assertThat(algorithm.isLightMode).isFalse()
-
-        algorithm.bundleAverageLightMode = 3.0
-        assertThat(algorithm.isLightMode).isFalse()
-
-        algorithm.bundleAverageLightMode = 0.0
-        assertThat(algorithm.isLightMode).isFalse()
-    }
-
-    @Test
-    fun shouldSetIsDarkModeToTrueWhenBundleAverageIsLessThanThreshold() {
-        // Note: [mockDarkModeThreshold] is 2.0.
-        algorithm.bundleAverageDarkMode = 1.9
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 1.0
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 0.0
-        assertThat(algorithm.isDarkMode).isTrue()
-
-        algorithm.bundleAverageDarkMode = 2.0
-        assertThat(algorithm.isDarkMode).isFalse()
-
-        algorithm.bundleAverageDarkMode = 3.0
-        assertThat(algorithm.isDarkMode).isFalse()
-
-        algorithm.bundleAverageDarkMode = 10.0
-        assertThat(algorithm.isDarkMode).isFalse()
-    }
-
-    @Test
-    fun shouldCorrectlyCalculateAverageFromABundle() {
-        // For light mode.
-        algorithm.bundleLightMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f)
-        assertThat(algorithm.bundleAverageLightMode).isEqualTo(4.0)
-
-        algorithm.bundleLightMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f)
-        assertThat(algorithm.bundleAverageLightMode).isEqualTo(5.0)
-
-        // For dark mode.
-        algorithm.bundleDarkMode = arrayListOf(1.0f, 3.0f, 5.0f, 7.0f, 9.0f)
-        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(5.0)
-
-        algorithm.bundleDarkMode = arrayListOf(2.0f, 4.0f, 6.0f, 8.0f, 10.0f)
-        assertThat(algorithm.bundleAverageDarkMode).isEqualTo(6.0)
-    }
-
-    @Test
-    fun shouldAddSensorEventUpdatesToBundles() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        // On start() one bundle is created for light and dark mode each.
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Add 1 more bundle to queue for each mode.
-        algorithm.bundlesQueueLightMode.add(ArrayList())
-        algorithm.bundlesQueueDarkMode.add(ArrayList())
-
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(2.0f)
-        algorithm.onUpdateLightSensorEvent(3.0f)
-        algorithm.onUpdateLightSensorEvent(4.0f)
-
-        val expectedValues = listOf(1.0f, 1.0f, 2.0f, 3.0f, 4.0f)
-
-        assertBundleContainsAll(algorithm.bundlesQueueLightMode[0], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueLightMode[1], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[0], expectedValues)
-        assertBundleContainsAll(algorithm.bundlesQueueDarkMode[1], expectedValues)
-    }
-
-    @Test
-    fun shouldCorrectlyEnqueueLightModeBundles() {
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(1)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(2)
-
-        algorithm.enqueueLightModeBundle.run()
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(3)
-
-        // Verifies dark mode bundles queue is not impacted.
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
-    }
-
-    @Test
-    fun shouldCorrectlyEnqueueDarkModeBundles() {
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(0)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(1)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(2)
-
-        algorithm.enqueueDarkModeBundle.run()
-        assertThat(algorithm.bundlesQueueDarkMode.size).isEqualTo(3)
-
-        // Verifies light mode bundles queue is not impacted.
-        assertThat(algorithm.bundlesQueueLightMode.size).isEqualTo(0)
-    }
-
-    @Test
-    fun shouldCorrectlyDequeueLightModeBundles() {
-        // Sets up the light mode bundles queue.
-        val bundle1 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
-        val bundle2 = arrayListOf(5.0f, 10f)
-        val bundle3 = arrayListOf(2.0f, 4.0f)
-        algorithm.bundlesQueueLightMode.add(bundle1)
-        algorithm.bundlesQueueLightMode.add(bundle2)
-        algorithm.bundlesQueueLightMode.add(bundle3)
-
-        // The committed bundle should be the first one in queue.
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle1)
-
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle2)
-
-        algorithm.dequeueLightModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleLightMode, bundle3)
-
-        // Verifies that the dark mode bundle is not impacted.
-        assertBundleContainsAll(algorithm.bundleDarkMode, listOf())
-    }
-
-    @Test
-    fun shouldCorrectlyDequeueDarkModeBundles() {
-        // Sets up the dark mode bundles queue.
-        val bundle1 = arrayListOf(2.0f, 4.0f)
-        val bundle2 = arrayListOf(5.0f, 10f)
-        val bundle3 = arrayListOf(1.0f, 3.0f, 6.0f, 9.0f)
-        algorithm.bundlesQueueDarkMode.add(bundle1)
-        algorithm.bundlesQueueDarkMode.add(bundle2)
-        algorithm.bundlesQueueDarkMode.add(bundle3)
-
-        // The committed bundle should be the first one in queue.
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle1)
-
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle2)
-
-        algorithm.dequeueDarkModeBundle.run()
-        assertBundleContainsAll(algorithm.bundleDarkMode, bundle3)
-
-        // Verifies that the light mode bundle is not impacted.
-        assertBundleContainsAll(algorithm.bundleLightMode, listOf())
-    }
-
-    @Test
-    fun shouldSetLightSensorLevelFromSensorEventUpdates() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(1.0f)
-
-        algorithm.onUpdateLightSensorEvent(10.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(10.0f)
-
-        algorithm.onUpdateLightSensorEvent(0.0f)
-        assertThat(algorithm.lightSensorLevel).isEqualTo(0.0f)
-    }
-
-    @Test
-    fun shouldRippleFromSensorEventUpdatesDownToAmbientLightMode() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Sensor event updates.
-        algorithm.onUpdateLightSensorEvent(10.0f)
-        algorithm.onUpdateLightSensorEvent(15.0f)
-        algorithm.onUpdateLightSensorEvent(12.0f)
-        algorithm.onUpdateLightSensorEvent(10.0f)
-
-        // Advances time so both light and dark claims have been calculated.
-        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
-
-        // Verifies the callback is triggered the ambient mode has changed LIGHT.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
-    }
-
-    @Test
-    fun shouldRippleFromSensorEventUpdatesDownToAmbientDarkMode() {
-        val callback = mock(AmbientLightModeMonitor.Callback::class.java)
-        algorithm.start(callback)
-        executor.runAllReady()
-
-        // Sensor event updates.
-        algorithm.onUpdateLightSensorEvent(1.0f)
-        algorithm.onUpdateLightSensorEvent(0.5f)
-        algorithm.onUpdateLightSensorEvent(1.2f)
-        algorithm.onUpdateLightSensorEvent(0.8f)
-
-        // Advances time so both light and dark claims have been calculated.
-        systemClock.advanceTime((mockLightModeSpan + 1).toLong())
-
-        // Verifies the callback is triggered the ambient mode has changed DARK.
-        verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
-    }
-
-    // Asserts that [bundle] contains the same elements as [expected], not necessarily in the same
-    // order.
-    private fun assertBundleContainsAll(bundle: ArrayList<Float>, expected: Collection<Float>) {
-        assertThat(bundle.size).isEqualTo(expected.size)
-        assertThat(bundle.containsAll(expected))
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index f4fa921..021f70e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -370,6 +370,7 @@
         assertThat(data.song).isEqualTo(SESSION_TITLE)
         assertThat(data.app).isEqualTo(APP_NAME)
         assertThat(data.actions).hasSize(1)
+        assertThat(data.semanticActions!!.playOrPause).isNotNull()
         assertThat(data.lastActive).isAtLeast(currentTime)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 421ae03..11326e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -59,6 +59,7 @@
     private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
     private Icon mIcon = mock(Icon.class);
     private IconCompat mIconCompat = mock(IconCompat.class);
+    private View mDialogLaunchView = mock(View.class);
 
     private MediaOutputAdapter mMediaOutputAdapter;
     private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
@@ -245,7 +246,7 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
         mViewHolder.mContainerLayout.performClick();
 
-        verify(mMediaOutputController).launchBluetoothPairing();
+        verify(mMediaOutputController).launchBluetoothPairing(mViewHolder.mContainerLayout);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index c5c4d79..2be30b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -45,7 +45,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,7 +67,6 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
-    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
     private MediaOutputController mMediaOutputController;
@@ -82,7 +80,7 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                 mMediaOutputController);
         mMediaOutputBaseDialogImpl.onCreate(new Bundle());
@@ -175,7 +173,7 @@
     class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
 
         MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
-            super(context, mediaOutputController, mDialogManager);
+            super(context, mediaOutputController);
 
             mAdapter = mMediaOutputBaseAdapter;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index bdc3117..789822e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -55,7 +55,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -94,7 +93,6 @@
     private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
-    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private Context mSpyContext;
     private MediaOutputController mMediaOutputController;
@@ -117,7 +115,7 @@
 
         mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -161,7 +159,7 @@
     public void start_withoutPackageName_verifyMediaControllerInit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
 
         mMediaOutputController.start(mCb);
 
@@ -182,7 +180,7 @@
     public void stop_withoutPackageName_verifyMediaControllerDeinit() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
 
         mMediaOutputController.start(mCb);
 
@@ -453,7 +451,7 @@
     public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
         mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotifCollection, mUiEventLogger, mDialogLaunchAnimator);
 
         assertThat(mMediaOutputController.getNotificationIcon()).isNull();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index ada8d35..8a3ea56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -40,7 +40,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -68,7 +67,6 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
-    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputDialog mMediaOutputDialog;
     private MediaOutputController mMediaOutputController;
@@ -78,10 +76,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger, mDialogManager);
+                mMediaOutputController, mUiEventLogger);
         mMediaOutputDialog.show();
 
         when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
@@ -127,7 +125,7 @@
     // and verify if the calling times increases.
     public void onCreate_ShouldLogVisibility() {
         MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
-                mMediaOutputController, mUiEventLogger, mDialogManager);
+                mMediaOutputController, mUiEventLogger);
         testDialog.show();
 
         testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index b114452..e8cd6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -67,7 +66,6 @@
             mock(NotificationEntryManager.class);
     private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
-    private final SystemUIDialogManager mDialogManager = mock(SystemUIDialogManager.class);
 
     private MediaOutputGroupDialog mMediaOutputGroupDialog;
     private MediaOutputController mMediaOutputController;
@@ -77,10 +75,10 @@
     public void setUp() {
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                 mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
-                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator, mDialogManager);
+                mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
-                mMediaOutputController, mDialogManager);
+                mMediaOutputController);
         mMediaOutputGroupDialog.show();
         when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 14afece..794bc09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -49,7 +48,6 @@
 import java.util.concurrent.Executor
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttCommandLineHelperTest : SysuiTestCase() {
 
     private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index f05d621..28de176 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
@@ -26,10 +25,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -39,39 +42,103 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerCommonTest : SysuiTestCase() {
     private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
 
+    private lateinit var fakeClock: FakeSystemClock
+    private lateinit var fakeExecutor: FakeExecutor
+
     private lateinit var appIconDrawable: Drawable
     @Mock
     private lateinit var windowManager: WindowManager
+    @Mock
+    private lateinit var tapGestureDetector: TapGestureDetector
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
-        controllerCommon = TestControllerCommon(context, windowManager)
+        appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        fakeClock = FakeSystemClock()
+        fakeExecutor = FakeExecutor(fakeClock)
+
+        controllerCommon = TestControllerCommon(
+            context, windowManager, fakeExecutor, tapGestureDetector
+        )
     }
 
     @Test
-    fun displayChip_chipAdded() {
+    fun displayChip_chipAddedAndGestureDetectionStarted() {
         controllerCommon.displayChip(getState())
 
         verify(windowManager).addView(any(), any())
+        verify(tapGestureDetector).addOnGestureDetectedCallback(any(), any())
     }
 
     @Test
-    fun displayChip_twice_chipNotAddedTwice() {
+    fun displayChip_twice_chipAndGestureDetectionNotAddedTwice() {
         controllerCommon.displayChip(getState())
         reset(windowManager)
+        reset(tapGestureDetector)
 
         controllerCommon.displayChip(getState())
         verify(windowManager, never()).addView(any(), any())
+        verify(tapGestureDetector, never()).addOnGestureDetectedCallback(any(), any())
     }
 
     @Test
-    fun removeChip_chipRemoved() {
+    fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
+
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_chipDisappearsAfterTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        val waitTime = 1000L
+        fakeClock.advanceTime(waitTime)
+        controllerCommon.displayChip(getState())
+
+        // Wait until the timeout for the first display would've happened
+        fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
+
+        // Verify we didn't hide the chip
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        fakeClock.advanceTime(1000L)
+        controllerCommon.displayChip(getState())
+
+        // Ensure we still hide the chip eventually
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
+    fun removeChip_chipRemovedAndGestureDetectionStopped() {
         // First, add the chip
         controllerCommon.displayChip(getState())
 
@@ -79,6 +146,7 @@
         controllerCommon.removeChip()
 
         verify(windowManager).removeView(any())
+        verify(tapGestureDetector).removeOnGestureDetectedCallback(any())
     }
 
     @Test
@@ -93,10 +161,10 @@
         controllerCommon.displayChip(getState())
         val chipView = getChipView()
 
-        val state = MediaTttChipState(PACKAGE_NAME)
+        val state = TestChipState(PACKAGE_NAME)
         controllerCommon.setIcon(state, chipView)
 
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
         assertThat(chipView.getAppIconView().contentDescription)
                 .isEqualTo(state.getAppName(context))
     }
@@ -113,13 +181,19 @@
 
     inner class TestControllerCommon(
         context: Context,
-        windowManager: WindowManager
+        windowManager: WindowManager,
+        @Main mainExecutor: DelayableExecutor,
+        tapGestureDetector: TapGestureDetector,
     ) : MediaTttChipControllerCommon<MediaTttChipState>(
-        context, windowManager, R.layout.media_ttt_chip
+        context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
     ) {
         override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
         }
     }
+
+    inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) {
+        override fun getAppIcon(context: Context) = appIconDrawable
+    }
 }
 
 private const val PACKAGE_NAME = "com.android.systemui"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 44f691c..e5f4df6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -17,9 +17,13 @@
 package com.android.systemui.media.taptotransfer.receiver
 
 import android.app.StatusBarManager
-import android.graphics.drawable.Icon
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
@@ -28,33 +32,59 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
 class MediaTttChipControllerReceiverTest : SysuiTestCase() {
     private lateinit var controllerReceiver: MediaTttChipControllerReceiver
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
         controllerReceiver = MediaTttChipControllerReceiver(
-                commandQueue, context, windowManager, Handler.getMain())
+            commandQueue,
+            context,
+            windowManager,
+            FakeExecutor(FakeSystemClock()),
+            TapGestureDetector(context),
+            Handler.getMain(),
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -113,13 +143,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-
+        assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
     }
 
     @Test
     fun displayChip_hasAppIconDrawable_iconIsDrawable() {
-        val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+        val drawable = context.getDrawable(R.drawable.ic_alarm)!!
         val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
 
         controllerReceiver.displayChip(state)
@@ -133,13 +162,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
     }
 
     @Test
     fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
-        val appName = "FakeAppName"
+        val appName = "Override App Name"
         val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
 
         controllerReceiver.displayChip(state)
@@ -156,6 +184,7 @@
     private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
 }
 
+private const val APP_NAME = "Fake app name"
 private const val PACKAGE_NAME = "com.android.systemui"
 
 private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index dc39893..e5ba3f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -17,7 +17,12 @@
 package com.android.systemui.media.taptotransfer.sender
 
 import android.app.StatusBarManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.view.View
 import android.view.WindowManager
 import android.widget.ImageView
@@ -28,32 +33,58 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
 class MediaTttChipControllerSenderTest : SysuiTestCase() {
     private lateinit var controllerSender: MediaTttChipControllerSender
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
+        controllerSender = MediaTttChipControllerSender(
+            commandQueue,
+            context,
+            windowManager,
+            FakeExecutor(FakeSystemClock()),
+            TapGestureDetector(context)
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -192,9 +223,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -207,9 +237,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -222,9 +251,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -237,9 +265,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -252,9 +279,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -314,9 +340,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -376,9 +401,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -451,15 +475,15 @@
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToStartCast() =
-        AlmostCloseToStartCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToEndCast() =
-        AlmostCloseToEndCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverTriggered() =
-        TransferToReceiverTriggered(PACKAGE_NAME, DEVICE_NAME)
+        TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceTriggered() =
@@ -468,23 +492,24 @@
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToReceiverSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToThisDeviceSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferFailed() = TransferFailed(PACKAGE_NAME)
 }
 
-private const val DEVICE_NAME = "My Tablet"
+private const val APP_NAME = "Fake app name"
+private const val OTHER_DEVICE_NAME = "My Tablet"
 private const val PACKAGE_NAME = "com.android.systemui"
 
-private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
     .addFeature("feature")
     .setPackageName(PACKAGE_NAME)
     .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 5a06048..6df56e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -19,6 +19,7 @@
 import android.app.WallpaperColors;
 import android.graphics.Color;
 import android.testing.AndroidTestingRunner;
+import android.util.Log;
 
 import androidx.test.filters.SmallTest;
 
@@ -29,7 +30,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -108,7 +113,7 @@
                 Style.SPRITZ /* style */);
         int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2);
         Cam cam = Cam.fromInt(primaryMid);
-        Assert.assertEquals(cam.getChroma(), 4.0, 1.0);
+        Assert.assertEquals(cam.getChroma(), 12.0, 1.0);
     }
 
     @Test
@@ -128,6 +133,41 @@
                 Style.EXPRESSIVE /* style */);
         int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
         Cam cam = Cam.fromInt(neutralMid);
-        Assert.assertEquals(cam.getChroma(), 16.0, 1.0);
+        Assert.assertEquals(cam.getChroma(), 12.0, 1.0);
+    }
+
+    /**
+     * Generate xml for SystemPaletteTest#testThemeStyles().
+     */
+    @Test
+    public void generateThemeStyles() {
+        StringBuilder xml = new StringBuilder();
+        for (int hue = 0; hue < 360; hue += 60) {
+            final int sourceColor = Cam.getInt(hue, 50f, 50f);
+            final String sourceColorHex = Integer.toHexString(sourceColor);
+
+            xml.append("    <theme color=\"").append(sourceColorHex).append("\">\n");
+
+            for (Style style : Style.values()) {
+                String styleName = style.name().toLowerCase();
+                ColorScheme colorScheme = new ColorScheme(sourceColor, false, style);
+                xml.append("        <").append(styleName).append(">");
+
+                List<String> colors = new ArrayList<>();
+                for (Stream<Integer> stream: Arrays.asList(colorScheme.getAccent1().stream(),
+                        colorScheme.getAccent2().stream(),
+                        colorScheme.getAccent3().stream(),
+                        colorScheme.getNeutral1().stream(),
+                        colorScheme.getNeutral2().stream())) {
+                    colors.add("ffffff");
+                    colors.addAll(stream.map(Integer::toHexString).map(s -> s.substring(2)).collect(
+                            Collectors.toList()));
+                }
+                xml.append(String.join(",", colors));
+                xml.append("</").append(styleName).append(">\n");
+            }
+            xml.append("    </theme>\n");
+        }
+        Log.d("ColorSchemeXml", xml.toString());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 73d2b0b..bb42c12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
@@ -90,6 +91,7 @@
                         mock(NavBarHelper.class),
                         mock(TaskbarDelegate.class),
                         mNavigationBarFactory,
+                        mock(StatusBarKeyguardViewManager.class),
                         mock(DumpManager.class),
                         mock(AutoHideController.class),
                         mock(LightBarController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 090ce43..48d3857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -28,6 +28,7 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
 import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
+import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -228,7 +229,8 @@
 
     @Test
     public void testHomeLongPress() {
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
         mNavigationBar.onHomeLongClick(mNavigationBar.getView());
 
         verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
@@ -241,7 +243,8 @@
                     .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
                     .build());
         when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
 
         mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
                 /*downTime=*/SystemClock.uptimeMillis(),
@@ -263,7 +266,8 @@
 
     @Test
     public void testRegisteredWithDispatcher() {
-        mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
+        mNavigationBar.onViewAttachedToWindow(mNavigationBar
+                .createView(null, /* initialVisibility= */ true));
         verify(mBroadcastDispatcher).registerReceiverWithHandler(
                 any(BroadcastReceiver.class),
                 any(IntentFilter.class),
@@ -283,8 +287,8 @@
         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
         doNothing().when(defaultNavBar).checkNavBarModes();
         doNothing().when(externalNavBar).checkNavBarModes();
-        defaultNavBar.createView(null);
-        externalNavBar.createView(null);
+        defaultNavBar.createView(null, /* initialVisibility= */ true);
+        externalNavBar.createView(null, /* initialVisibility= */ true);
 
         defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
                 BACK_DISPOSITION_DEFAULT, true);
@@ -318,7 +322,7 @@
         doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
         doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
         doNothing().when(mNavigationBar).checkNavBarModes();
-        mNavigationBar.createView(null);
+        mNavigationBar.createView(null, /* initialVisibility= */ true);
         WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
         doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
 
@@ -354,7 +358,7 @@
 
     @Test
     public void testA11yEventAfterDetach() {
-        View v = mNavigationBar.createView(null);
+        View v = mNavigationBar.createView(null, /* initialVisibility= */ true);
         mNavigationBar.onViewAttachedToWindow(v);
         verify(mNavBarHelper).registerNavTaskStateUpdater(any(
                 NavBarHelper.NavbarTaskbarStateUpdater.class));
@@ -366,6 +370,20 @@
         mNavigationBar.updateAccessibilityStateFlags();
     }
 
+    @Test
+    public void testCreateView_initiallyVisible_viewIsVisible() {
+        mNavigationBar.createView(null, /* initialVisibility= */ true);
+
+        assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void testCreateView_initiallyNotVisible_viewIsNotVisible() {
+        mNavigationBar.createView(null, /* initialVisibility= */ false);
+
+        assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
     private NavigationBar createNavBar(Context context) {
         DeviceProvisionedController deviceProvisionedController =
                 mock(DeviceProvisionedController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 511848d..3508226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -24,7 +24,7 @@
 import android.content.pm.UserInfo
 import android.os.Process.SYSTEM_UID
 import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -68,7 +68,8 @@
         private const val ENT_USER_ID = 10
 
         private const val TEST_PACKAGE_NAME = "test package name"
-        private const val TEST_ATTRIBUTION = "test attribution"
+        private const val TEST_ATTRIBUTION_TAG = "test attribution tag"
+        private const val TEST_PROXY_LABEL = "test proxy label"
 
         private const val PERM_CAMERA = android.Manifest.permission_group.CAMERA
         private const val PERM_MICROPHONE = android.Manifest.permission_group.MICROPHONE
@@ -109,12 +110,12 @@
 
     private val dialogProvider = object : PrivacyDialogController.DialogProvider {
         var list: List<PrivacyDialog.PrivacyElement>? = null
-        var starter: ((String, Int) -> Unit)? = null
+        var starter: ((String, Int, CharSequence?, Intent?) -> Unit)? = null
 
         override fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String, Int) -> Unit
+            starter: (String, Int, CharSequence?, Intent?) -> Unit
         ): PrivacyDialog {
             this.list = list
             this.starter = starter
@@ -125,13 +126,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
         nextUid = 0
-
         setUpDefaultMockResponses()
 
         controller = PrivacyDialogController(
-            permissionManager,
+                permissionManager,
                 packageManager,
                 privacyItemController,
                 userTracker,
@@ -248,40 +247,54 @@
         val usage = createMockPermGroupUsage(
                 packageName = TEST_PACKAGE_NAME,
                 uid = generateUidForUser(USER_ID),
-                permGroupName = PERM_CAMERA,
-                lastAccess = 5L,
+                permissionGroupName = PERM_CAMERA,
+                lastAccessTimeMillis = 5L,
                 isActive = true,
                 isPhoneCall = false,
-                attribution = TEST_ATTRIBUTION
+                attributionTag = null,
+                proxyLabel = TEST_PROXY_LABEL
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
 
         controller.showDialog(context)
         exhaustExecutors()
 
-        val expected = PrivacyDialog.PrivacyElement(
-                type = PrivacyType.TYPE_CAMERA,
-                packageName = TEST_PACKAGE_NAME,
-                userId = USER_ID,
-                applicationName = TEST_PACKAGE_NAME,
-                attribution = TEST_ATTRIBUTION,
-                lastActiveTimestamp = 5L,
-                active = true,
-                phoneCall = false,
-                enterprise = false
-        )
-        assertThat(dialogProvider.list).containsExactly(expected)
+        dialogProvider.list?.let { list ->
+            assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+            assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).userId).isEqualTo(USER_ID)
+            assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).attributionTag).isNull()
+            assertThat(list.get(0).attributionLabel).isNull()
+            assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+            assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+            assertThat(list.get(0).active).isTrue()
+            assertThat(list.get(0).phoneCall).isFalse()
+            assertThat(list.get(0).enterprise).isFalse()
+            assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+            assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+                    controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+                    .isTrue()
+        }
+    }
+
+    private fun isIntentEqual(actual: Intent, expected: Intent): Boolean {
+        return actual.action == expected.action &&
+                actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
+                expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) &&
+                actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
+                expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle
     }
 
     @Test
     fun testTwoElementsDifferentType_sorted() {
         val usage_camera = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_camera",
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_microphone",
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_microphone, usage_camera)
@@ -322,12 +335,12 @@
         val usage_active = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_active",
                 isActive = true,
-                lastAccess = 0L
+                lastAccessTimeMillis = 0L
         )
         val usage_active_moreRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_active_recent",
                 isActive = true,
-                lastAccess = 1L
+                lastAccessTimeMillis = 1L
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_active, usage_active_moreRecent)
@@ -344,17 +357,17 @@
         val usage_recent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_recent",
                 isActive = false,
-                lastAccess = 0L
+                lastAccessTimeMillis = 0L
         )
         val usage_moreRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_moreRecent",
                 isActive = false,
-                lastAccess = 1L
+                lastAccessTimeMillis = 1L
         )
         val usage_mostRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_mostRecent",
                 isActive = false,
-                lastAccess = 2L
+                lastAccessTimeMillis = 2L
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_recent, usage_mostRecent, usage_moreRecent)
@@ -370,13 +383,13 @@
     @Test
     fun testMicAndCameraDisabled() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -394,13 +407,13 @@
     @Test
     fun testLocationDisabled() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -420,13 +433,13 @@
     @Test
     fun testAllIndicatorsAvailable() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -444,13 +457,13 @@
     @Test
     fun testNoIndicatorsAvailable() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -500,7 +513,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter)
                 .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
 
@@ -518,7 +531,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID, null, null)
         verify(activityStarter)
                 .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
 
@@ -533,7 +546,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP)
@@ -548,7 +561,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED)
@@ -578,7 +591,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
                 USER_ID, TEST_PACKAGE_NAME)
     }
@@ -599,6 +612,42 @@
         verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
     }
 
+    @Test
+    fun testInvalidAttributionTag() {
+        val usage = createMockPermGroupUsage(
+                packageName = TEST_PACKAGE_NAME,
+                uid = generateUidForUser(USER_ID),
+                permissionGroupName = PERM_CAMERA,
+                lastAccessTimeMillis = 5L,
+                isActive = true,
+                isPhoneCall = false,
+                attributionTag = "INVALID_ATTRIBUTION_TAG",
+                proxyLabel = TEST_PROXY_LABEL
+        )
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+
+        controller.showDialog(context)
+        exhaustExecutors()
+
+        dialogProvider.list?.let { list ->
+            assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+            assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).userId).isEqualTo(USER_ID)
+            assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).attributionTag).isEqualTo("INVALID_ATTRIBUTION_TAG")
+            assertThat(list.get(0).attributionLabel).isNull()
+            assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+            assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+            assertThat(list.get(0).active).isTrue()
+            assertThat(list.get(0).phoneCall).isFalse()
+            assertThat(list.get(0).enterprise).isFalse()
+            assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+            assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+                    controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+                    .isTrue()
+        }
+    }
+
     private fun exhaustExecutors() {
         FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
     }
@@ -608,9 +657,7 @@
         `when`(appOpsController.isMicMuted).thenReturn(false)
 
         `when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
-                .thenAnswer {
-                    FakeApplicationInfo(it.getArgument(0))
-                }
+                .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
 
         `when`(privacyItemController.locationAvailable).thenReturn(true)
         `when`(privacyItemController.micCameraAvailable).thenReturn(true)
@@ -636,21 +683,24 @@
     private fun createMockPermGroupUsage(
         packageName: String = TEST_PACKAGE_NAME,
         uid: Int = generateUidForUser(USER_ID),
-        permGroupName: String = PERM_CAMERA,
-        lastAccess: Long = 0L,
+        permissionGroupName: String = PERM_CAMERA,
+        lastAccessTimeMillis: Long = 0L,
         isActive: Boolean = false,
         isPhoneCall: Boolean = false,
-        attribution: CharSequence? = null
-    ): PermGroupUsage {
-        val usage = mock(PermGroupUsage::class.java)
+        attributionTag: CharSequence? = null,
+        attributionLabel: CharSequence? = null,
+        proxyLabel: CharSequence? = null
+    ): PermissionGroupUsage {
+        val usage = mock(PermissionGroupUsage::class.java)
         `when`(usage.packageName).thenReturn(packageName)
         `when`(usage.uid).thenReturn(uid)
-        `when`(usage.permGroupName).thenReturn(permGroupName)
-        `when`(usage.lastAccess).thenReturn(lastAccess)
+        `when`(usage.permissionGroupName).thenReturn(permissionGroupName)
+        `when`(usage.lastAccessTimeMillis).thenReturn(lastAccessTimeMillis)
         `when`(usage.isActive).thenReturn(isActive)
         `when`(usage.isPhoneCall).thenReturn(isPhoneCall)
-        `when`(usage.attribution).thenReturn(attribution)
-
+        `when`(usage.attributionTag).thenReturn(attributionTag)
+        `when`(usage.attributionLabel).thenReturn(attributionLabel)
+        `when`(usage.proxyLabel).thenReturn(proxyLabel)
         return usage
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 1baaf6b..c714fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -34,6 +34,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import android.content.Intent
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -43,11 +44,11 @@
     companion object {
         private const val TEST_PACKAGE_NAME = "test_pkg"
         private const val TEST_USER_ID = 0
+        private const val TEST_PERM_GROUP = "test_perm_group"
     }
 
     @Mock
-    private lateinit var starter: (String, Int) -> Unit
-
+    private lateinit var starter: (String, Int, CharSequence?, Intent?) -> Unit
     private lateinit var dialog: PrivacyDialog
 
     @Before
@@ -71,16 +72,20 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         false,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 )
         )
         dialog = PrivacyDialog(context, list, starter)
         dialog.show()
         dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
-        verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
+        verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID, null, null)
     }
 
     @Test
@@ -115,10 +120,14 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         true,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 ),
                 PrivacyDialog.PrivacyElement(
                         PrivacyType.TYPE_MICROPHONE,
@@ -126,10 +135,14 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         false,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 )
         )
         dialog = PrivacyDialog(context, list, starter)
@@ -145,10 +158,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 true,
                 false,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -171,10 +188,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -197,10 +218,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 true,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -219,10 +244,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -241,10 +270,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -255,17 +288,21 @@
     }
 
     @Test
-    fun testAttribution() {
+    fun testProxyLabel() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
                 TEST_PACKAGE_NAME,
                 TEST_USER_ID,
                 "App",
-                "attribution",
+                null,
+                null,
+                "proxyLabel",
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -274,7 +311,65 @@
         assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
                 context.getString(
                         R.string.ongoing_privacy_dialog_attribution_text,
-                        element.attribution
+                        element.proxyLabel
+                )
+        )
+    }
+
+    @Test
+    fun testSubattribution() {
+        val element = PrivacyDialog.PrivacyElement(
+                PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
+                "App",
+                null,
+                "For subattribution",
+                null,
+                0L,
+                true,
+                false,
+                false,
+                TEST_PERM_GROUP,
+                null
+        )
+
+        val list = listOf(element)
+        dialog = PrivacyDialog(context, list, starter)
+        dialog.show()
+        assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+                context.getString(
+                        R.string.ongoing_privacy_dialog_attribution_label,
+                        element.attributionLabel
+                )
+        )
+    }
+
+    @Test
+    fun testSubattributionAndProxyLabel() {
+        val element = PrivacyDialog.PrivacyElement(
+                PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
+                "App",
+                null,
+                "For subattribution",
+                "proxy label",
+                0L,
+                true,
+                false,
+                false,
+                TEST_PERM_GROUP,
+                null
+        )
+
+        val list = listOf(element)
+        dialog = PrivacyDialog(context, list, starter)
+        dialog.show()
+        assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+                context.getString(
+                        R.string.ongoing_privacy_dialog_attribution_proxy_label,
+                        element.attributionLabel, element.proxyLabel
                 )
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 92743ae5..30a5308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -1,31 +1,45 @@
 package com.android.systemui.qs
 
 import android.content.Context
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyDialogController
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
 
+    companion object {
+        const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+    }
+
     @Mock
     private lateinit var privacyItemController: PrivacyItemController
     @Mock
@@ -38,11 +52,20 @@
     private lateinit var privacyLogger: PrivacyLogger
     @Mock
     private lateinit var iconContainer: StatusIconContainer
+    @Mock
+    private lateinit var permissionManager: PermissionManager
+    @Mock
+    private lateinit var backgroundExecutor: Executor
+    @Mock
+    private lateinit var activityStarter: ActivityStarter
+    @Mock
+    private lateinit var appOpsController: AppOpsController
 
+    private val uiExecutor = FakeExecutor(FakeSystemClock())
+    private lateinit var deviceConfigProxy: DeviceConfigProxy
     private lateinit var cameraSlotName: String
     private lateinit var microphoneSlotName: String
     private lateinit var locationSlotName: String
-
     private lateinit var controller: HeaderPrivacyIconsController
 
     @Before
@@ -54,6 +77,7 @@
         cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
         microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
         locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
+        deviceConfigProxy = DeviceConfigProxyFake()
 
         controller = HeaderPrivacyIconsController(
                 privacyItemController,
@@ -61,7 +85,13 @@
                 privacyChip,
                 privacyDialogController,
                 privacyLogger,
-                iconContainer
+                iconContainer,
+                permissionManager,
+                backgroundExecutor,
+                uiExecutor,
+                activityStarter,
+                appOpsController,
+                deviceConfigProxy
         )
     }
 
@@ -111,18 +141,38 @@
 
     @Test
     fun testPrivacyChipClicked() {
+        changeProperty(SAFETY_CENTER_ENABLED, false)
         controller.onParentVisible()
 
         val captor = argumentCaptor<View.OnClickListener>()
         verify(privacyChip).setOnClickListener(capture(captor))
-
         captor.value.onClick(privacyChip)
 
         verify(privacyDialogController).showDialog(any(Context::class.java))
     }
 
+    @Test
+    fun testSafetyCenterFlag() {
+        changeProperty(SAFETY_CENTER_ENABLED, true)
+        controller.onParentVisible()
+        val captor = argumentCaptor<View.OnClickListener>()
+        verify(privacyChip).setOnClickListener(capture(captor))
+        captor.value.onClick(privacyChip)
+        verify(privacyDialogController, never()).showDialog(any(Context::class.java))
+    }
+
     private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
         whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
         whenever(privacyItemController.locationAvailable).thenReturn(location)
     }
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+            DeviceConfig.NAMESPACE_PRIVACY,
+            name,
+            value?.toString(),
+            false
+        )
+        uiExecutor.runAllReady()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 0d65541..a2959e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -136,6 +136,8 @@
     private LocationController mLocationController;
     @Mock
     private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock
+    private View mDialogLaunchView;
 
     private TestableResources mTestableResources;
     private InternetDialogController mInternetDialogController;
@@ -384,7 +386,8 @@
 
     @Test
     public void launchWifiNetworkDetailsSetting_withNoWifiEntryKey_doNothing() {
-        mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */);
+        mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */,
+                mDialogLaunchView);
 
         verify(mActivityStarter, never())
                 .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
@@ -392,9 +395,11 @@
 
     @Test
     public void launchWifiNetworkDetailsSetting_withWifiEntryKey_startActivity() {
-        mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key");
+        mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key",
+                mDialogLaunchView);
 
-        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(),
+                any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index ed35dcb..cf97bda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -458,7 +458,8 @@
     public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
         mSeeAll.performClick();
 
-        verify(mInternetDialogController).launchNetworkSetting();
+        verify(mInternetDialogController).launchNetworkSetting(
+                mDialogView.requireViewById(R.id.see_all_layout));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 8695b29..030c65a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -21,6 +21,7 @@
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.view.View
+import android.widget.Button
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -63,6 +64,8 @@
     @Mock
     private lateinit var launchView: View
     @Mock
+    private lateinit var neutralButton: Button
+    @Mock
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
@@ -130,14 +133,17 @@
 
         controller.showDialog(launchView)
 
-        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
+        verify(dialog)
+            .setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
+        `when`(dialog.getButton(DialogInterface.BUTTON_NEUTRAL)).thenReturn(neutralButton)
 
         clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
         verify(activityStarter)
                 .postStartActivityDismissingKeyguard(
                         argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
-                        eq(0)
+                        eq(0),
+                        eq(null)
                 )
         verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
     }
@@ -148,7 +154,8 @@
 
         controller.showDialog(launchView)
 
-        verify(dialog).setNeutralButton(anyInt(), capture(clickCaptor))
+        verify(dialog)
+            .setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
 
         clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 466d954..65271bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -779,8 +779,11 @@
     public void testOnKeyguardShowingChanged_showing_updatesPersistentMessages() {
         createController();
 
-        // GIVEN keyguard is showing
+        // GIVEN keyguard is showing and not dozing
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mController.setVisible(true);
+        mExecutor.runAllReady();
+        reset(mRotateTextViewController);
 
         // WHEN keyguard showing changed called
         mKeyguardStateControllerCallback.onKeyguardShowingChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 7fafb24..407044b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -54,7 +54,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -121,7 +120,6 @@
                 mock(KeyguardBypassController.class),
                 Optional.of(mock(Bubbles.class)),
                 mock(DynamicPrivacyController.class),
-                mock(ForegroundServiceSectionController.class),
                 mock(DynamicChildBindController.class),
                 mock(LowPriorityInflationHelper.class),
                 mock(AssistantFeedbackController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index d13451d..03c22b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -155,4 +155,19 @@
         mIconView.getIcon(largeIcon);
         // no crash? good
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testNullIcon() {
+        Icon mockIcon = mock(Icon.class);
+        when(mockIcon.loadDrawableAsUser(any(), anyInt())).thenReturn(null);
+        mStatusBarIcon.icon = mockIcon;
+        mIconView.set(mStatusBarIcon);
+
+        Bitmap bitmap = Bitmap.createBitmap(60, 60, Bitmap.Config.ARGB_8888);
+        Icon icon = Icon.createWithBitmap(bitmap);
+        StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+                icon, 0, 0, "");
+        mIconView.getIcon(largeIcon);
+        // No crash? good
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index ecc2a1b..b4cae38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -63,6 +63,7 @@
                 commandRegistry, batteryController, configurationController,
                 featureFlags, context, windowManager, systemClock, uiEventLogger)
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
+        controller.registerCallbacks()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 9f152e1..b7b3088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -56,6 +56,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        allowTestableLooperAsMainThread();
         when(mWifiInfo.makeCopy(anyLong())).thenReturn(mWifiInfo);
         when(mWifiInfo.isPrimary()).thenReturn(true);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
new file mode 100644
index 0000000..c038903
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -0,0 +1,130 @@
+package com.android.systemui.statusbar.gesture
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.InputEvent
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class GenericGestureDetectorTest : SysuiTestCase() {
+
+    private lateinit var gestureDetector: TestGestureDetector
+
+    @Before
+    fun setUp() {
+        gestureDetector = TestGestureDetector()
+    }
+
+    @Test
+    fun noCallbacksRegistered_notGestureListening() {
+        assertThat(gestureDetector.isGestureListening).isFalse()
+    }
+
+    @Test
+    fun callbackRegistered_isGestureListening() {
+        gestureDetector.addOnGestureDetectedCallback("tag"){}
+
+        assertThat(gestureDetector.isGestureListening).isTrue()
+    }
+
+    @Test
+    fun multipleCallbacksRegistered_isGestureListening() {
+        gestureDetector.addOnGestureDetectedCallback("tag"){}
+        gestureDetector.addOnGestureDetectedCallback("tag2"){}
+
+        assertThat(gestureDetector.isGestureListening).isTrue()
+    }
+
+    @Test
+    fun allCallbacksUnregistered_notGestureListening() {
+        gestureDetector.addOnGestureDetectedCallback("tag"){}
+        gestureDetector.addOnGestureDetectedCallback("tag2"){}
+
+        gestureDetector.removeOnGestureDetectedCallback("tag")
+        gestureDetector.removeOnGestureDetectedCallback("tag2")
+
+        assertThat(gestureDetector.isGestureListening).isFalse()
+    }
+
+    @Test
+    fun someButNotAllCallbacksUnregistered_isGestureListening() {
+        gestureDetector.addOnGestureDetectedCallback("tag"){}
+        gestureDetector.addOnGestureDetectedCallback("tag2"){}
+
+        gestureDetector.removeOnGestureDetectedCallback("tag2")
+
+        assertThat(gestureDetector.isGestureListening).isTrue()
+    }
+
+    @Test
+    fun onInputEvent_meetsGestureCriteria_allCallbacksNotified() {
+        var callbackNotified = false
+        gestureDetector.addOnGestureDetectedCallback("tag"){
+            callbackNotified = true
+        }
+
+        gestureDetector.onInputEvent(
+            MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0)
+        )
+
+        assertThat(callbackNotified).isTrue()
+    }
+
+    @Test
+    fun onInputEvent_doesNotMeetGestureCriteria_callbackNotNotified() {
+        var callbackNotified = false
+        gestureDetector.addOnGestureDetectedCallback("tag"){
+            callbackNotified = true
+        }
+
+        gestureDetector.onInputEvent(
+            MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X - 5, 0f, 0)
+        )
+
+        assertThat(callbackNotified).isFalse()
+    }
+
+    @Test
+    fun callbackUnregisteredThenGestureDetected_oldCallbackNotNotified() {
+        var oldCallbackNotified = false
+        gestureDetector.addOnGestureDetectedCallback("tag"){
+            oldCallbackNotified = true
+        }
+        gestureDetector.addOnGestureDetectedCallback("tag2"){}
+
+        gestureDetector.removeOnGestureDetectedCallback("tag")
+        gestureDetector.onInputEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0))
+
+        assertThat(oldCallbackNotified).isFalse()
+    }
+
+    inner class TestGestureDetector : GenericGestureDetector("fakeTag") {
+        var isGestureListening = false
+
+        override fun onInputEvent(ev: InputEvent) {
+            if (ev is MotionEvent && ev.x == CORRECT_X) {
+                onGestureDetected()
+            }
+        }
+
+        override fun startGestureListening() {
+            super.startGestureListening()
+            isGestureListening = true
+        }
+
+        override fun stopGestureListening() {
+            super.stopGestureListening()
+            isGestureListening = false
+        }
+    }
+}
+
+private const val CORRECT_X = 1234f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f2b7bf5..0fff5f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -201,7 +201,6 @@
                 () -> mNotificationRowBinder,
                 () -> mRemoteInputManager,
                 mLeakDetector,
-                mock(ForegroundServiceDismissalFeatureController.class),
                 mock(IStatusBarService.class),
                 NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
                 mock(DumpManager.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
index 3820b98..3b908b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
@@ -20,10 +20,14 @@
 import android.view.Choreographer
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.withArgCaptor
+import dagger.BindsInstance
+import dagger.Component
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,8 +45,10 @@
         whenever(it.executeDelayed(any(), anyLong())).thenReturn(timeoueSubscription)
     }
 
-    val pipelineChoreographer: NotifPipelineChoreographer = NotifPipelineChoreographerModule
-            .provideChoreographer(viewChoreographer, executor)
+    val pipelineChoreographer: NotifPipelineChoreographer =
+        DaggerNotifPipelineChoreographerTestComponent.factory()
+                .create(viewChoreographer, executor)
+                .choreographer
 
     @Test
     fun scheduleThenEvalFrameCallback() {
@@ -97,4 +103,19 @@
         verify(viewChoreographer).removeFrameCallback(frameCallback)
         verify(timeoueSubscription).run()
     }
+}
+
+@SysUISingleton
+@Component(modules = [NotifPipelineChoreographerModule::class])
+interface NotifPipelineChoreographerTestComponent {
+
+    val choreographer: NotifPipelineChoreographer
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance viewChoreographer: Choreographer,
+            @BindsInstance @Main executor: DelayableExecutor
+        ): NotifPipelineChoreographerTestComponent
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 3f84c16..a2d8e3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -28,13 +28,16 @@
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import dagger.BindsInstance
+import dagger.Component
 import org.junit.Test
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -50,9 +53,16 @@
     val statusBarStateController: StatusBarStateController = mock()
     val keyguardStateController: KeyguardStateController = mock()
 
-    val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule
-            .provideCoordinator(dynamicPrivacyController, lockscreenUserManager,
-            keyguardUpdateMonitor, statusBarStateController, keyguardStateController)
+    val coordinator: SensitiveContentCoordinator =
+        DaggerTestSensitiveContentCoordinatorComponent
+                .factory()
+                .create(
+                        dynamicPrivacyController,
+                        lockscreenUserManager,
+                        keyguardUpdateMonitor,
+                        statusBarStateController,
+                        keyguardStateController)
+                .coordinator
 
     @Test
     fun onDynamicPrivacyChanged_invokeInvalidationListener() {
@@ -238,4 +248,21 @@
             override fun getRepresentativeEntry(): NotificationEntry = mockEntry
         }
     }
+}
+
+@CoordinatorScope
+@Component(modules = [SensitiveContentCoordinatorModule::class])
+interface TestSensitiveContentCoordinatorComponent {
+    val coordinator: SensitiveContentCoordinator
+
+    @Component.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance dynamicPrivacyController: DynamicPrivacyController,
+            @BindsInstance lockscreenUserManager: NotificationLockscreenUserManager,
+            @BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor,
+            @BindsInstance statusBarStateController: StatusBarStateController,
+            @BindsInstance keyguardStateController: KeyguardStateController
+        ): TestSensitiveContentCoordinatorComponent
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
index 24a0ad3..bc54bf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -18,6 +18,8 @@
 
 import static android.view.DragEvent.ACTION_DRAG_STARTED;
 
+import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -36,7 +38,12 @@
 import org.junit.runner.RunWith;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -58,6 +65,7 @@
     private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
     private NotificationMenuRowPlugin.MenuItem mMenuItem =
             mock(NotificationMenuRowPlugin.MenuItem.class);
+    private ShadeController mShadeController = mock(ShadeController.class);
 
     @Before
     public void setUp() throws Exception {
@@ -69,11 +77,15 @@
                 mContext,
                 mDependency,
                 TestableLooper.get(this));
-        mRow = mNotificationTestHelper.createRow();
+        mRow = spy(mNotificationTestHelper.createRow());
+        Notification notification = mRow.getEntry().getSbn().getNotification();
+        notification.contentIntent = mock(PendingIntent.class);
+        doReturn(true).when(mRow).startDragAndDrop(any(), any(), any(), anyInt());
         mGroupRow = mNotificationTestHelper.createGroup(4);
         when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
 
-        mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager);
+        mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager,
+                mShadeController);
     }
 
     @Test
@@ -86,10 +98,6 @@
         mRow.doLongClickCallback(0, 0);
         mRow.doDragCallback(0, 0);
         verify(controller).startDragAndDrop(mRow);
-
-        // Simulate the drag start
-        mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
-                null, null, false));
         verify(mHeadsUpManager, times(1)).releaseAllImmediately();
     }
 
@@ -98,14 +106,27 @@
         ExpandableNotificationRowDragController controller = createSpyController();
         mRow.setDragController(controller);
 
-        mDependency.get(ShadeController.class).instantExpandNotificationsPanel();
+        mRow.doDragCallback(0, 0);
+        verify(controller).startDragAndDrop(mRow);
+        verify(mShadeController).animateCollapsePanels(eq(0), eq(true),
+                eq(false), anyFloat());
+    }
+
+    @Test
+    public void testDoStartDrag_noLaunchIntent() throws Exception {
+        ExpandableNotificationRowDragController controller = createSpyController();
+        mRow.setDragController(controller);
+
+        // Clear the intents
+        Notification notification = mRow.getEntry().getSbn().getNotification();
+        notification.contentIntent = null;
+        notification.fullScreenIntent = null;
+
         mRow.doDragCallback(0, 0);
         verify(controller).startDragAndDrop(mRow);
 
-        // Simulate the drag start
-        mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
-                null, null, false));
-        verify(mDependency.get(ShadeController.class)).animateCollapsePanels(0, true);
+        // Verify that we never start the actual drag since there is no content
+        verify(mRow, never()).startDragAndDrop(any(), any(), any(), anyInt());
     }
 
     private ExpandableNotificationRowDragController createSpyController() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 52189e4..7fc5ece 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -61,7 +61,6 @@
 import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -191,7 +190,6 @@
                 () -> mRowBinder,
                 () -> mRemoteInputManager,
                 mLeakDetector,
-                mock(ForegroundServiceDismissalFeatureController.class),
                 mock(IStatusBarService.class),
                 NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
                 mock(DumpManager.class)
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 c4f954f..6b93ae2 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
@@ -58,7 +58,6 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -68,7 +67,6 @@
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -129,9 +127,6 @@
     @Mock private IStatusBarService mIStatusBarService;
     @Mock private UiEventLogger mUiEventLogger;
     @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
-    @Mock private ForegroundServiceSectionController mFgServicesSectionController;
-    @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
     @Mock private LayoutInflater mLayoutInflater;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private VisualStabilityManager mVisualStabilityManager;
@@ -151,8 +146,6 @@
 
         when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
         when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
-        when(mFgServicesSectionController.createView(mLayoutInflater))
-                .thenReturn(mForegroundServiceDungeonView);
 
         mController = new NotificationStackScrollLayoutController(
                 true,
@@ -187,8 +180,6 @@
                 mLockscreenShadeTransitionController,
                 mIStatusBarService,
                 mUiEventLogger,
-                mFgFeatureController,
-                mFgServicesSectionController,
                 mLayoutInflater,
                 mRemoteInputManager,
                 mVisualStabilityManager,
@@ -399,22 +390,6 @@
     }
 
     @Test
-    public void testForegroundDismissEnabled() {
-        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
-                mForegroundServiceDungeonView);
-    }
-
-    @Test
-    public void testForegroundDismissaDisabled() {
-        when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
-        mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
-                any(ForegroundServiceDungeonView.class));
-    }
-
-    @Test
     public void testUpdateFooter_remoteInput() {
         ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
                 ArgumentCaptor.forClass(RemoteInputController.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 38d7ce7..6ce3b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -59,6 +59,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -97,7 +98,7 @@
                 mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
                 mBatteryController, mScrimController, () -> mBiometricUnlockController,
                 mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
-                mKeyguardUpdateMonitor, mPulseExpansionHandler,
+                mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
                 mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
                 mAuthController, mNotificationIconAreaController);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index f6eff82..479c271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -100,8 +100,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.idle.IdleHostViewController;
-import com.android.systemui.idle.dagger.IdleViewComponent;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
@@ -266,12 +264,6 @@
     @Mock
     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     @Mock
-    private IdleViewComponent.Factory mIdleViewComponentFactory;
-    @Mock
-    private IdleViewComponent mIdleViewComponent;
-    @Mock
-    private IdleHostViewController mIdleHostViewController;
-    @Mock
     private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
     @Mock
     private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@@ -475,10 +467,6 @@
                 .thenReturn(mCommunalViewComponent);
         when(mCommunalViewComponent.getCommunalHostViewController())
                 .thenReturn(mCommunalHostViewController);
-        when(mIdleViewComponentFactory.build(any()))
-                .thenReturn(mIdleViewComponent);
-        when(mIdleViewComponent.getIdleHostViewController())
-                .thenReturn(mIdleHostViewController);
         when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
                 .thenReturn(mKeyguardStatusView);
         when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
@@ -523,7 +511,6 @@
                 mKeyguardUserSwitcherComponentFactory,
                 mKeyguardStatusBarViewComponentFactory,
                 mCommunalViewComponentFactory,
-                mIdleViewComponentFactory,
                 mLockscreenShadeTransitionController,
                 mGroupManager,
                 mNotificationAreaController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
index 12e71af..34a5d4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.dock.DockManager
+import com.android.systemui.lowlightclock.LowLightClockController
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationShadeDepthController
 import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -38,11 +39,13 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.anyFloat
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import java.util.Optional
 import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
@@ -79,6 +82,8 @@
     private lateinit var mLockIconViewController: LockIconViewController
     @Mock
     private lateinit var mPhoneStatusBarViewController: PhoneStatusBarViewController
+    @Mock
+    private lateinit var mLowLightClockController: LowLightClockController
 
     private lateinit var mInteractionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
     private lateinit var mInteractionEventHandler: InteractionEventHandler
@@ -101,7 +106,8 @@
             stackScrollLayoutController,
             mStatusBarKeyguardViewManager,
             mStatusBarWindowStateController,
-            mLockIconViewController
+            mLockIconViewController,
+            Optional.of(mLowLightClockController)
         )
         mController.setupExpandedStatusBar()
         mController.setService(mStatusBar, mNotificationShadeWindowController)
@@ -235,6 +241,31 @@
         verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
         assertThat(returnVal).isTrue()
     }
+
+    @Test
+    fun testLowLightClockAttachedWhenExpandedStatusBarSetup() {
+        verify(mLowLightClockController).attachLowLightClockView(ArgumentMatchers.any())
+    }
+
+    @Test
+    fun testLowLightClockShownWhenDozing() {
+        mController.setDozing(true)
+        verify(mLowLightClockController).showLowLightClock(true)
+    }
+
+    @Test
+    fun testLowLightClockDozeTimeTickCalled() {
+        mController.dozeTimeTick()
+        verify(mLowLightClockController).dozeTimeTick()
+    }
+
+    @Test
+    fun testLowLightClockHiddenWhenNotDozing() {
+        mController.setDozing(true)
+        verify(mLowLightClockController).showLowLightClock(true)
+        mController.setDozing(false)
+        verify(mLowLightClockController).showLowLightClock(false)
+    }
 }
 
 private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index d885da8..6c33113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.lowlightclock.LowLightClockController;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -56,6 +57,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -79,6 +82,7 @@
     @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
     @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @Mock private LockIconViewController mLockIconViewController;
+    @Mock private LowLightClockController mLowLightClockController;
 
     @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
             mInteractionEventHandlerCaptor;
@@ -110,7 +114,8 @@
                 mNotificationStackScrollLayoutController,
                 mStatusBarKeyguardViewManager,
                 mStatusBarWindowStateController,
-                mLockIconViewController);
+                mLockIconViewController,
+                Optional.of(mLowLightClockController));
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar, mNotificationShadeWindowController);
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 107ba81..8b93de5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,6 +61,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -100,6 +103,8 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
+    private SysUIUnfoldComponent mSysUiUnfoldComponent;
+    @Mock
     private DreamOverlayStateController mDreamOverlayStateController;
     @Mock
     private LatencyTracker mLatencyTracker;
@@ -130,6 +135,7 @@
                 mock(NotificationMediaManager.class),
                 mKeyguardBouncerFactory,
                 mKeyguardMessageAreaFactory,
+                Optional.of(mSysUiUnfoldComponent),
                 () -> mShadeController,
                 mLatencyTracker);
         mStatusBarKeyguardViewManager.registerStatusBar(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index a7809c2..c7db9e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -116,6 +116,7 @@
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.charging.WiredChargingRippleController;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -288,6 +289,7 @@
     @Mock private InteractionJankMonitor mJankMonitor;
     @Mock private DeviceStateManager mDeviceStateManager;
     @Mock private DreamOverlayStateController mDreamOverlayStateController;
+    @Mock private WiredChargingRippleController mWiredChargingRippleController;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -479,7 +481,8 @@
                 mNotifPipelineFlags,
                 mJankMonitor,
                 mDeviceStateManager,
-                mDreamOverlayStateController);
+                mDreamOverlayStateController,
+                mWiredChargingRippleController);
         when(mKeyguardViewMediator.registerStatusBar(
                 any(StatusBar.class),
                 any(NotificationPanelViewController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 0920cac..1c48eca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -105,6 +105,7 @@
         val notificationCollection = mock(CommonNotifCollection::class.java)
 
         controller = OngoingCallController(
+                context,
                 notificationCollection,
                 mockOngoingCallFlags,
                 clock,
@@ -204,17 +205,48 @@
 
     /** Regression test for b/194731244. */
     @Test
-    fun onEntryUpdated_calledManyTimes_uidObserverUnregisteredManyTimes() {
-        val numCalls = 4
-
-        for (i in 0 until numCalls) {
+    fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() {
+        for (i in 0 until 4) {
             // Re-create the notification each time so that it's considered a different object and
-            // observers will get re-registered (and hopefully unregistered).
+            // will re-trigger the whole flow.
             notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         }
 
-        // There should be 1 observer still registered, so we should unregister n-1 times.
-        verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any())
+        verify(mockIActivityManager, times(1))
+            .registerUidObserver(any(), any(), any(), any())
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun entryUpdated_getUidProcessStateThrowsException_noCrash() {
+        `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+                .thenThrow(SecurityException())
+
+        // No assert required, just check no crash
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun entryUpdated_registerUidObserverThrowsException_noCrash() {
+        `when`(mockIActivityManager.registerUidObserver(
+            any(), any(), any(), nullable(String::class.java)
+        )).thenThrow(SecurityException())
+
+        // No assert required, just check no crash
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+    }
+
+    /** Regression test for b/216248574. */
+    @Test
+    fun entryUpdated_packageNameProvidedToActivityManager() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
+        verify(mockIActivityManager).registerUidObserver(
+            any(), any(), any(), packageNameCaptor.capture()
+        )
+        assertThat(packageNameCaptor.value).isNotNull()
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index f5554c5..9f9cb40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -205,6 +205,12 @@
         mTestableLooper.processAllMessages();
 
         verify(callback, times(1)).onLocationActiveChanged(false);
+
+        when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
+        mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+                "com.google.android.googlequicksearchbox", true);
+        mTestableLooper.processAllMessages();
+        verify(callback, times(1)).onLocationActiveChanged(true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
new file mode 100644
index 0000000..d4be881
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.user
+
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class UserSwitcherActivityTest : SysuiTestCase() {
+    @Mock
+    private lateinit var activity: UserSwitcherActivity
+    @Mock
+    private lateinit var userSwitcherController: UserSwitcherController
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var layoutInflater: LayoutInflater
+    @Mock
+    private lateinit var falsingManager: FalsingManager
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var shadeController: ShadeController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        activity = UserSwitcherActivity(
+            userSwitcherController,
+            broadcastDispatcher,
+            layoutInflater,
+            falsingManager,
+            userManager,
+            shadeController
+        )
+    }
+
+    @Test
+    fun testMaxColumns() {
+        assertThat(activity.getMaxColumns(3)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(4)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(5)).isEqualTo(3)
+        assertThat(activity.getMaxColumns(6)).isEqualTo(3)
+        assertThat(activity.getMaxColumns(7)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(9)).isEqualTo(5)
+    }
+}
diff --git a/services/Android.bp b/services/Android.bp
index e0ca8a6..2e4405f 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -54,7 +54,9 @@
         SYSTEM_OPTIMIZE_JAVA: {
             optimize: {
                 enabled: true,
-                optimize: true,
+                // TODO(b/210510433): Enable optimizations after improving
+                // retracing infra.
+                optimize: false,
                 shrink: true,
                 proguard_flags_files: ["proguard.flags"],
             },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 62da981..0e99265 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1363,8 +1363,18 @@
      * </p>
      *
      * @param displayId The logical display id
-     * @param region the new magnified region, may be empty if
-     *               magnification is not enabled (e.g. scale is 1)
+     * @param region The magnification region.
+     *               If the config mode is
+     *               {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+     *               it is the region of the screen currently active for magnification.
+     *               the returned region will be empty if the magnification is not active
+     *               (e.g. scale is 1. And the magnification is active if magnification
+     *               gestures are enabled or if a service is running that can control
+     *               magnification.
+     *               If the config mode is
+     *               {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+     *               it is the region of screen projected on the magnification window.
+     *               The region will be empty if magnification is not activated.
      * @param config The magnification config. That has magnification mode, the new scale and the
      *              new screen-relative center position
      */
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index fe97a46..a958209 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -50,7 +50,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
-import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -374,9 +373,8 @@
                     .setScale(getScale())
                     .setCenterX(getCenterX())
                     .setCenterY(getCenterY()).build();
-            mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId,
-                    mMagnificationRegion,
-                    config);
+            mMagnificationInfoChangedCallback.onFullScreenMagnificationChanged(mDisplayId,
+                    mMagnificationRegion, config);
             if (mUnregisterPending && !isMagnifying()) {
                 unregister(mDeleteAfterUnregister);
             }
@@ -665,10 +663,10 @@
      * FullScreenMagnificationController Constructor
      */
     public FullScreenMagnificationController(@NonNull Context context,
-            @NonNull AccessibilityManagerService ams, @NonNull Object lock,
+            @NonNull AccessibilityTraceManager traceManager, @NonNull Object lock,
             @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
             @NonNull MagnificationScaleProvider scaleProvider) {
-        this(new ControllerContext(context, ams,
+        this(new ControllerContext(context, traceManager,
                 LocalServices.getService(WindowManagerInternal.class),
                 new Handler(context.getMainLooper()),
                 context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
@@ -1521,7 +1519,6 @@
     @VisibleForTesting
     public static class ControllerContext {
         private final Context mContext;
-        private final AccessibilityManagerService mAms;
         private final AccessibilityTraceManager mTrace;
         private final WindowManagerInternal mWindowManager;
         private final Handler mHandler;
@@ -1531,13 +1528,12 @@
          * Constructor for ControllerContext.
          */
         public ControllerContext(@NonNull Context context,
-                @NonNull AccessibilityManagerService ams,
+                @NonNull AccessibilityTraceManager traceManager,
                 @NonNull WindowManagerInternal windowManager,
                 @NonNull Handler handler,
                 long animationDuration) {
             mContext = context;
-            mAms = ams;
-            mTrace = ams.getTraceManager();
+            mTrace = traceManager;
             mWindowManager = windowManager;
             mHandler = handler;
             mAnimationDuration = animationDuration;
@@ -1552,14 +1548,6 @@
         }
 
         /**
-         * @return AccessibilityManagerService
-         */
-        @NonNull
-        public AccessibilityManagerService getAms() {
-            return mAms;
-        }
-
-        /**
          * @return AccessibilityTraceManager
          */
         @NonNull
@@ -1632,5 +1620,17 @@
          *                           hidden.
          */
         void onImeWindowVisibilityChanged(boolean shown);
+
+        /**
+         * Called when the magnification spec changed.
+         *
+         * @param displayId The logical display id
+         * @param region    The region of the screen currently active for magnification.
+         *                  The returned region will be empty if the magnification is not active.
+         * @param config    The magnification config. That has magnification mode, the new scale and
+         *                  the new screen-relative center position
+         */
+        void onFullScreenMagnificationChanged(int displayId, @NonNull Region region,
+                @NonNull MagnificationConfig config);
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index c376bf8..09e82c7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -405,6 +405,12 @@
         mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
     }
 
+    @Override
+    public void onFullScreenMagnificationChanged(int displayId, @NonNull Region region,
+            @NonNull MagnificationConfig config) {
+        mAms.notifyMagnificationChanged(displayId, region, config);
+    }
+
     private void disableFullScreenMagnificationIfNeeded(int displayId) {
         final FullScreenMagnificationController fullScreenMagnificationController =
                 getFullScreenMagnificationController();
@@ -590,7 +596,7 @@
         synchronized (mLock) {
             if (mFullScreenMagnificationController == null) {
                 mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
-                        mAms, mLock, this, mScaleProvider);
+                        mAms.getTraceManager(), mLock, this, mScaleProvider);
             }
         }
         return mFullScreenMagnificationController;
diff --git a/services/autofill/OWNERS b/services/autofill/OWNERS
index c52751d..edfb211 100644
--- a/services/autofill/OWNERS
+++ b/services/autofill/OWNERS
@@ -1 +1 @@
-include /core/java/android/service/autofill/OWNERS
+include /core/java/android/view/autofill/OWNERS
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 76ee728..e0fa67f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -103,7 +103,6 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -367,8 +366,6 @@
     @Nullable
     private ClientSuggestionsSession mClientSuggestionsSession;
 
-    private final AccessibilityManager mAccessibilityManager;
-
     // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
     // new one per Session.
     private final BroadcastReceiver mDelayedFillBroadcastReceiver =
@@ -518,10 +515,7 @@
                     return;
                 }
 
-                // If a11y touch exploration is enabled, then we do not send an inline fill request
-                // to the regular af service, because dropdown UI is easier to use.
-                if (mPendingInlineSuggestionsRequest.isServiceSupported()
-                        && !mAccessibilityManager.isTouchExplorationEnabled()) {
+                if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
                     mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
                             mPendingFillRequest.getFillContexts(),
                             mPendingFillRequest.getClientState(),
@@ -1064,7 +1058,6 @@
         mRemoteFillService = serviceComponentName == null ? null
                 : new RemoteFillService(context, serviceComponentName, userId, this,
                         bindInstantServiceAllowed);
-        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
         mUiLatencyHistory = uiLatencyHistory;
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index e122993..75d9b7e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -275,12 +275,10 @@
             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);
+                    if (sDebug) {
+                        Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+                                + "service didn't provide a presentation for it on " + dataset);
+                    }
                     continue;
                 }
                 final View view;
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
index 8963337..7e3ede1 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -22,20 +22,18 @@
 import android.app.backup.RestoreSet;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
-import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.infra.AndroidFuture;
 
 import java.util.ArrayDeque;
-import java.util.Deque;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Queue;
-import java.util.concurrent.ArrayBlockingQueue;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -49,17 +47,19 @@
 
     private final IBackupTransport mTransportBinder;
     private final TransportStatusCallbackPool mCallbackPool;
+    private final TransportFutures mTransportFutures;
 
     BackupTransportClient(IBackupTransport transportBinder) {
         mTransportBinder = transportBinder;
         mCallbackPool = new TransportStatusCallbackPool();
+        mTransportFutures = new TransportFutures();
     }
 
     /**
      * See {@link IBackupTransport#name()}.
      */
     public String name() throws RemoteException {
-        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.name(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -68,7 +68,7 @@
      * See {@link IBackupTransport#configurationIntent()}
      */
     public Intent configurationIntent() throws RemoteException {
-        AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.configurationIntent(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -77,7 +77,7 @@
      * See {@link IBackupTransport#currentDestinationString()}
      */
     public String currentDestinationString() throws RemoteException {
-        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.currentDestinationString(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -86,7 +86,7 @@
      * See {@link IBackupTransport#dataManagementIntent()}
      */
     public Intent dataManagementIntent() throws RemoteException {
-        AndroidFuture<Intent> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Intent> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.dataManagementIntent(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -96,7 +96,7 @@
      */
     @Nullable
     public CharSequence dataManagementIntentLabel() throws RemoteException {
-        AndroidFuture<CharSequence> resultFuture = new AndroidFuture<>();
+        AndroidFuture<CharSequence> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.dataManagementIntentLabel(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -105,7 +105,7 @@
      * See {@link IBackupTransport#transportDirName()}
      */
     public String transportDirName() throws RemoteException {
-        AndroidFuture<String> resultFuture = new AndroidFuture<>();
+        AndroidFuture<String> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.transportDirName(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -153,7 +153,7 @@
      * See {@link IBackupTransport#requestBackupTime()}
      */
     public long requestBackupTime() throws RemoteException {
-        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.requestBackupTime(resultFuture);
         Long result = getFutureResult(resultFuture);
         return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -177,7 +177,7 @@
      * See {@link IBackupTransport#getAvailableRestoreSets()}
      */
     public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
-        AndroidFuture<List<RestoreSet>> resultFuture = new AndroidFuture<>();
+        AndroidFuture<List<RestoreSet>> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.getAvailableRestoreSets(resultFuture);
         List<RestoreSet> result = getFutureResult(resultFuture);
         return result == null ? null : result.toArray(new RestoreSet[] {});
@@ -187,7 +187,7 @@
      * See {@link IBackupTransport#getCurrentRestoreSet()}
      */
     public long getCurrentRestoreSet() throws RemoteException {
-        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.getCurrentRestoreSet(resultFuture);
         Long result = getFutureResult(resultFuture);
         return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -210,7 +210,7 @@
      * See {@link IBackupTransport#nextRestorePackage()}
      */
     public RestoreDescription nextRestorePackage() throws RemoteException {
-        AndroidFuture<RestoreDescription> resultFuture = new AndroidFuture<>();
+        AndroidFuture<RestoreDescription> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.nextRestorePackage(resultFuture);
         return getFutureResult(resultFuture);
     }
@@ -245,7 +245,7 @@
      * See {@link IBackupTransport#requestFullBackupTime()}
      */
     public long requestFullBackupTime() throws RemoteException {
-        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.requestFullBackupTime(resultFuture);
         Long result = getFutureResult(resultFuture);
         return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -309,7 +309,7 @@
      */
     public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
             throws RemoteException {
-        AndroidFuture<Boolean> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Boolean> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.isAppEligibleForBackup(targetPackage, isFullBackup, resultFuture);
         Boolean result = getFutureResult(resultFuture);
         return result != null && result;
@@ -319,7 +319,7 @@
      * See {@link IBackupTransport#getBackupQuota(String, boolean)}
      */
     public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
-        AndroidFuture<Long> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Long> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.getBackupQuota(packageName, isFullBackup, resultFuture);
         Long result = getFutureResult(resultFuture);
         return result == null ? BackupTransport.TRANSPORT_ERROR : result;
@@ -355,18 +355,58 @@
      * See {@link IBackupTransport#getTransportFlags()}
      */
     public int getTransportFlags() throws RemoteException {
-        AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+        AndroidFuture<Integer> resultFuture = mTransportFutures.newFuture();
         mTransportBinder.getTransportFlags(resultFuture);
         Integer result = getFutureResult(resultFuture);
         return result == null ? BackupTransport.TRANSPORT_ERROR : result;
     }
 
+    /**
+     * Allows the {@link TransportConnection} to notify this client
+     * if the underlying transport has become unusable.  If that happens
+     * we want to cancel all active futures or callbacks.
+     */
+    void onBecomingUnusable() {
+        mCallbackPool.cancelActiveCallbacks();
+        mTransportFutures.cancelActiveFutures();
+    }
+
     private <T> T getFutureResult(AndroidFuture<T> future) {
         try {
             return future.get(600, TimeUnit.SECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
             Slog.w(TAG, "Failed to get result from transport:", e);
             return null;
+        } finally {
+            mTransportFutures.remove(future);
+        }
+    }
+
+    private static class TransportFutures {
+        private final Object mActiveFuturesLock = new Object();
+        private final Set<AndroidFuture<?>> mActiveFutures = new HashSet<>();
+
+        <T> AndroidFuture<T> newFuture() {
+            AndroidFuture<T> future = new AndroidFuture<>();
+            synchronized (mActiveFuturesLock) {
+                mActiveFutures.add(future);
+            }
+            return future;
+        }
+
+        <T> void remove(AndroidFuture<T> future) {
+            synchronized (mActiveFuturesLock) {
+                mActiveFutures.remove(future);
+            }
+        }
+
+        void cancelActiveFutures() {
+            synchronized (mActiveFuturesLock) {
+                for (AndroidFuture<?> future : mActiveFutures) {
+                    future.cancel(true);
+                }
+                mActiveFutures.clear();
+            }
         }
     }
 
@@ -375,27 +415,47 @@
 
         private final Object mPoolLock = new Object();
         private final Queue<TransportStatusCallback> mCallbackPool = new ArrayDeque<>();
+        private final Set<TransportStatusCallback> mActiveCallbacks = new HashSet<>();
 
         TransportStatusCallback acquire() {
             synchronized (mPoolLock) {
-                if (mCallbackPool.isEmpty()) {
-                    return new TransportStatusCallback();
-                } else {
-                    return mCallbackPool.poll();
+                TransportStatusCallback callback = mCallbackPool.poll();
+                if (callback == null) {
+                    callback = new TransportStatusCallback();
                 }
+                callback.reset();
+                mActiveCallbacks.add(callback);
+                return callback;
             }
         }
 
         void recycle(TransportStatusCallback callback) {
             synchronized (mPoolLock) {
+                mActiveCallbacks.remove(callback);
                 if (mCallbackPool.size() > MAX_POOL_SIZE) {
                     Slog.d(TAG, "TransportStatusCallback pool size exceeded");
                     return;
                 }
-
-                callback.reset();
                 mCallbackPool.add(callback);
             }
         }
+
+        void cancelActiveCallbacks() {
+            synchronized (mPoolLock) {
+                for (TransportStatusCallback callback : mActiveCallbacks) {
+                    try {
+                        callback.onOperationCompleteWithStatus(BackupTransport.TRANSPORT_ERROR);
+                        // This waits for status to propagate before the callback is reset.
+                        callback.getOperationStatus();
+                    } catch (RemoteException ex) {
+                        // Nothing we can do.
+                    }
+                    if (mCallbackPool.size() < MAX_POOL_SIZE) {
+                        mCallbackPool.add(callback);
+                    }
+                }
+                mActiveCallbacks.clear();
+            }
+        }
     }
 }
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
index f9a3c36..1009787 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnection.java
@@ -449,6 +449,9 @@
     private void onServiceDisconnected() {
         synchronized (mStateLock) {
             log(Priority.ERROR, "Service disconnected: client UNUSABLE");
+            if (mTransport != null) {
+                mTransport.onBecomingUnusable();
+            }
             setStateLocked(State.UNUSABLE, null);
             try {
                 // After unbindService() no calls back to mConnection
@@ -473,6 +476,9 @@
             checkStateIntegrityLocked();
 
             log(Priority.ERROR, "Binding died: client UNUSABLE");
+            if (mTransport != null) {
+                mTransport.onBecomingUnusable();
+            }
             // After unbindService() no calls back to mConnection
             switch (mState) {
                 case State.UNUSABLE:
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
index a55178c..bc5cb02 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStatusCallback.java
@@ -75,13 +75,11 @@
             }
 
             Slog.w(TAG, "Couldn't get operation status from transport");
-            return BackupTransport.TRANSPORT_ERROR;
         } catch (InterruptedException e) {
             Slog.w(TAG, "Couldn't get operation status from transport: ", e);
-            return BackupTransport.TRANSPORT_ERROR;
-        } finally {
-            reset();
         }
+
+        return BackupTransport.TRANSPORT_ERROR;
     }
 
     synchronized void reset() {
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index efa026b..e10151d 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -137,6 +137,8 @@
 import com.android.server.backup.utils.BackupObserverUtils;
 import com.android.server.backup.utils.SparseArrayUtils;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import com.google.android.collect.Sets;
 
 import java.io.BufferedInputStream;
@@ -4072,6 +4074,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private void dumpInternal(PrintWriter pw) {
         // Add prefix for only non-system users so that system user dumpsys is the same as before
         String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":";
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index daead0a..874f6fe 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -109,7 +109,7 @@
         @Override
         public void search(@NonNull SearchRequest searchRequest,
                 @NonNull ICloudSearchManagerCallback callBack) {
-            searchRequest.setSource(
+            searchRequest.setCallerPackageName(
                     mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
             runForUserLocked("search", searchRequest.getRequestId(), (service) ->
                     service.onSearchLocked(searchRequest, callBack));
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 21a677b8..cb28254 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -171,12 +171,20 @@
         broadcastChange(CHANGE_TYPE_REMOVED, association);
     }
 
+    /**
+     * @return a "snapshot" of the current state of the existing associations.
+     */
     public @NonNull Collection<AssociationInfo> getAssociations() {
-        final Collection<AssociationInfo> allAssociations;
         synchronized (mLock) {
-            allAssociations = mIdMap.values();
+            // IMPORTANT: make and return a COPY of the mIdMap.values(), NOT a "direct" reference.
+            // The HashMap.values() returns a collection which is backed by the HashMap, so changes
+            // to the HashMap are reflected in this collection.
+            // For us this means that if mIdMap is modified while the iteration over mIdMap.values()
+            // is in progress it may lead to "undefined results" (according to the HashMap's
+            // documentation) or cause ConcurrentModificationExceptions in the iterator (according
+            // to the bugreports...).
+            return List.copyOf(mIdMap.values());
         }
-        return Collections.unmodifiableCollection(allAssociations);
     }
 
     public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index be1bc79..c39b59a 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -120,6 +121,12 @@
             mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
         }
 
+        if (serviceConnectors.isEmpty()) {
+            Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: "
+                    + packageName + ". Please check if they are correctly declared.");
+            return;
+        }
+
         // The first connector in the list is always the primary connector: set a listener to it.
         serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
 
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index a771e7b..33301b1 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -228,11 +228,23 @@
 
         if (DEBUG) Log.i(TAG, "stopScan()");
         if (!mScanning) {
-            Log.d(TAG, "  > not scanning.");
+            if (DEBUG) Log.d(TAG, "  > not scanning.");
             return;
         }
+        // mScanCallback is non-null here - it cannot be null when mScanning is true.
 
-        mBleScanner.stopScan(mScanCallback);
+        // BluetoothLeScanner will throw an IllegalStateException if stopScan() is called while LE
+        // is not enabled.
+        if (mBtAdapter.isLeEnabled()) {
+            try {
+                mBleScanner.stopScan(mScanCallback);
+            } catch (RuntimeException e) {
+                // Just to be sure not to crash system server here if BluetoothLeScanner throws
+                // another RuntimeException.
+                Slog.w(TAG, "Exception while stopping BLE scanning", e);
+            }
+        }
+
         mScanning = false;
     }
 
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 4afa96c..bc1f28d 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -37,6 +37,7 @@
 import android.view.Display;
 import android.window.DisplayWindowPolicyController;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.BlockedAppStreamingActivity;
 
 import java.util.List;
@@ -75,9 +76,11 @@
     private final ArraySet<ComponentName> mAllowedActivities;
     @Nullable
     private final ArraySet<ComponentName> mBlockedActivities;
+    private final Object mGenericWindowPolicyControllerLock = new Object();
     private Consumer<ActivityInfo> mActivityBlockedCallback;
 
     @NonNull
+    @GuardedBy("mGenericWindowPolicyControllerLock")
     final ArraySet<Integer> mRunningUids = new ArraySet<>();
     @Nullable private final ActivityListener mActivityListener;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -149,11 +152,13 @@
 
     @Override
     public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
-        mRunningUids.clear();
-        mRunningUids.addAll(runningUids);
-        if (mActivityListener != null && mRunningUids.isEmpty()) {
-            // Post callback on the main thread so it doesn't block activity launching
-            mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mRunningUids.clear();
+            mRunningUids.addAll(runningUids);
+            if (mActivityListener != null && mRunningUids.isEmpty()) {
+                // Post callback on the main thread so it doesn't block activity launching
+                mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+            }
         }
         if (mRunningAppsChangedListener != null) {
             mRunningAppsChangedListener.onRunningAppsChanged(runningUids);
@@ -165,7 +170,9 @@
      * this controller.
      */
     boolean containsUid(int uid) {
-        return mRunningUids.contains(uid);
+        synchronized (mGenericWindowPolicyControllerLock) {
+            return mRunningUids.contains(uid);
+        }
     }
 
     private boolean canContainActivity(ActivityInfo activityInfo, int windowFlags,
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 387d911..c0a904f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -235,6 +235,10 @@
                 });
                 mPerDisplayWakelocks.clear();
             }
+            if (mVirtualAudioController != null) {
+                mVirtualAudioController.stopListening();
+                mVirtualAudioController = null;
+            }
         }
         mListener.onClose(mAssociationInfo.getId());
         mAppToken.unlinkToDeath(this, 0);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index e335a16..e6953f0 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -52,6 +52,7 @@
 import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -66,7 +67,7 @@
  *
  * @hide Only for use within the system server.
  */
-public abstract class PackageManagerInternal implements PackageSettingsSnapshotProvider {
+public abstract class PackageManagerInternal {
     @IntDef(prefix = "PACKAGE_", value = {
             PACKAGE_SYSTEM,
             PACKAGE_SETUP_WIZARD,
@@ -656,6 +657,11 @@
     public abstract void notifyPackageUse(String packageName, int reason);
 
     /**
+     * Notify the package is force stopped.
+     */
+    public abstract void onPackageProcessKilledForUninstall(String packageName);
+
+    /**
      * Returns a package object for the given package name.
      */
     public abstract @Nullable AndroidPackage getPackage(@NonNull String packageName);
@@ -718,7 +724,8 @@
     /**
      * Returns a package object for the disabled system package name.
      */
-    public abstract @Nullable PackageSetting getDisabledSystemPackage(@NonNull String packageName);
+    public abstract @Nullable PackageStateInternal getDisabledSystemPackage(
+            @NonNull String packageName);
 
     /**
      * Returns the package name for the disabled system package.
@@ -1334,4 +1341,12 @@
     public abstract PackageStateMutator.Result commitPackageStateMutation(
             @Nullable PackageStateMutator.InitialState state,
             @NonNull Consumer<PackageStateMutator> consumer);
+
+    /**
+     * @return package data snapshot for use with other PackageManager infrastructure. This should
+     * only be used as a parameter passed to another PM related class. Do not call methods on this
+     * directly.
+     */
+    @NonNull
+    public abstract PackageDataSnapshot snapshot();
 }
diff --git a/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java b/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java
deleted file mode 100644
index 221f172..0000000
--- a/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java
+++ /dev/null
@@ -1,77 +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.content.pm;
-
-import android.annotation.NonNull;
-
-import com.android.internal.util.FunctionalUtils;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.PackageSetting;
-import com.android.server.pm.pkg.PackageStateInternal;
-
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/** @hide */
-public interface PackageSettingsSnapshotProvider {
-
-    /**
-     * Run a function block that requires access to {@link PackageStateInternal} data. This will
-     * ensure the {@link PackageManagerService} lock is taken before any caller's internal lock
-     * to avoid deadlock. Note that this method may or may not lock. If a snapshot is available
-     * and valid, it will iterate the snapshot set of data.
-     */
-    void withPackageSettingsSnapshot(
-            @NonNull Consumer<Function<String, PackageStateInternal>> block);
-
-    /**
-     * Variant which returns a value to the caller.
-     * @see #withPackageSettingsSnapshot(Consumer)
-     */
-    <Output> Output withPackageSettingsSnapshotReturning(
-            @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
-                    Output> block);
-
-    /**
-     * Variant which throws.
-     * @see #withPackageSettingsSnapshot(Consumer)
-     */
-    <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
-            @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageStateInternal>,
-                    ExceptionType> block) throws ExceptionType;
-
-    /**
-     * Variant which throws 2 exceptions.
-     * @see #withPackageSettingsSnapshot(Consumer)
-     */
-    <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
-            withPackageSettingsSnapshotThrowing2(
-                    @NonNull FunctionalUtils.ThrowingChecked2Consumer<
-                            Function<String, PackageStateInternal>,
-                            ExceptionOne, ExceptionTwo> block)
-            throws ExceptionOne, ExceptionTwo;
-
-    /**
-     * Variant which returns a value to the caller and throws.
-     * @see #withPackageSettingsSnapshot(Consumer)
-     */
-    <Output, ExceptionType extends Exception> Output
-            withPackageSettingsSnapshotReturningThrowing(
-                    @NonNull FunctionalUtils.ThrowingCheckedFunction<
-                            Function<String, PackageStateInternal>, Output, ExceptionType> block)
-            throws ExceptionType;
-}
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 52a0aa7..1f8ef82 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -18,14 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
 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.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemProperties;
@@ -36,11 +44,8 @@
 import com.android.internal.os.IBinaryTransparencyService;
 import com.android.internal.util.FrameworkStatsLog;
 
-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;
@@ -53,6 +58,7 @@
  */
 public class BinaryTransparencyService extends SystemService {
     private static final String TAG = "TransparencyService";
+    private static final String EXTRA_SERVICE = "service";
 
     @VisibleForTesting
     static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
@@ -372,10 +378,77 @@
             Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
             getVBMetaDigestInformation();
 
-            // due to potentially long computation that may hold up boot time, SHA256 computations
-            // for APEXs and Modules will be executed via threads.
-            Slog.i(TAG, "Executing APEX & Module digest computations");
-            computeApexAndModuleDigests();
+            // due to potentially long computation that holds up boot time, computations for
+            // SHA256 digests of APEX and Module packages are scheduled here,
+            // but only executed when device is idle.
+            Slog.i(TAG, "Scheduling APEX and Module measurements to be updated.");
+            UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
+                    BinaryTransparencyService.this);
+        }
+    }
+
+    /**
+     * JobService to update binary measurements and update internal cache.
+     */
+    public static class UpdateMeasurementsJobService extends JobService {
+        private static final int COMPUTE_APEX_MODULE_SHA256_JOB_ID =
+                BinaryTransparencyService.UpdateMeasurementsJobService.class.hashCode();
+
+        @Override
+        public boolean onStartJob(JobParameters params) {
+            Slog.d(TAG, "Job to update binary measurements started.");
+            if (params.getJobId() != COMPUTE_APEX_MODULE_SHA256_JOB_ID) {
+                return false;
+            }
+
+            // we'll still update the measurements via threads to be mindful of low-end devices
+            // where this operation might take longer than expected, and so that we don't block
+            // system_server's main thread.
+            Executors.defaultThreadFactory().newThread(() -> {
+                // since we can't call updateBinaryMeasurements() directly, calling
+                // getApexInfo() achieves the same effect, and we simply discard the return
+                // value
+
+                IBinder b = ServiceManager.getService(Context.BINARY_TRANSPARENCY_SERVICE);
+                IBinaryTransparencyService iBtsService =
+                        IBinaryTransparencyService.Stub.asInterface(b);
+                try {
+                    iBtsService.getApexInfo();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Updating binary measurements was interrupted.", e);
+                    return;
+                }
+                jobFinished(params, false);
+            }).start();
+
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters params) {
+            return false;
+        }
+
+        @SuppressLint("DefaultLocale")
+        static void scheduleBinaryMeasurements(Context context, BinaryTransparencyService service) {
+            Slog.i(TAG, "Scheduling APEX & Module SHA256 digest computation job");
+            final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+            if (jobScheduler == null) {
+                Slog.e(TAG, "Failed to obtain an instance of JobScheduler.");
+                return;
+            }
+
+            final JobInfo jobInfo = new JobInfo.Builder(COMPUTE_APEX_MODULE_SHA256_JOB_ID,
+                    new ComponentName(context, UpdateMeasurementsJobService.class))
+                    .setRequiresDeviceIdle(true)
+                    .build();
+            if (jobScheduler.schedule(jobInfo) != JobScheduler.RESULT_SUCCESS) {
+                Slog.e(TAG, "Failed to schedule job to update binary measurements.");
+                return;
+            }
+            Slog.d(TAG, String.format(
+                    "Job %d to update binary measurements scheduled successfully.",
+                    COMPUTE_APEX_MODULE_SHA256_JOB_ID));
         }
     }
 
@@ -385,15 +458,9 @@
         FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
     }
 
-    private void computeApexAndModuleDigests() {
-        // using Executors will allow the computations to be done asynchronously, thus not holding
-        // up boot time.
-        Executors.defaultThreadFactory().newThread(() -> updateBinaryMeasurements()).start();
-    }
-
     @NonNull
     private List<PackageInfo> getInstalledApexs() {
-        List<PackageInfo> results = new ArrayList<PackageInfo>();
+        List<PackageInfo> results = new ArrayList<>();
         PackageManager pm = mContext.getPackageManager();
         if (pm == null) {
             Slog.e(TAG, "Error obtaining an instance of PackageManager.");
@@ -444,7 +511,7 @@
                     entry.setValue(packageInfo.lastUpdateTime);
 
                     // compute the digest for the updated package
-                    String sha256digest = computeSha256DigestOfFile(
+                    String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
                             packageInfo.applicationInfo.sourceDir);
                     if (sha256digest == null) {
                         Slog.e(TAG, "Failed to compute SHA256sum for file at "
@@ -481,7 +548,7 @@
             ApplicationInfo appInfo = packageInfo.applicationInfo;
 
             // compute SHA256 for these APEXs
-            String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+            String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir);
             if (sha256digest == null) {
                 Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                         packageInfo.packageName));
@@ -516,7 +583,8 @@
                 ApplicationInfo appInfo = packageInfo.applicationInfo;
 
                 // compute SHA256 digest for these modules
-                String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir);
+                String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
+                        appInfo.sourceDir);
                 if (sha256digest == null) {
                     Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                             packageName));
@@ -535,16 +603,4 @@
         }
     }
 
-    @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/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index aae1cc0..c375c73 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
@@ -32,6 +34,9 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -51,7 +56,7 @@
     final private static boolean localLOGV = DEBUG || false;
     final private static boolean localVerificationLOGV = DEBUG || false;
 
-    public void addFilter(F f) {
+    public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
         IntentFilter intentFilter = getIntentFilter(f);
         if (localLOGV) {
             Slog.v(TAG, "Adding filter: " + f);
@@ -420,8 +425,9 @@
         return Collections.unmodifiableSet(mFilters);
     }
 
-    public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
-            ArrayList<F[]> listCut, int userId) {
+    public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
+            String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
+            long customFlags) {
         ArrayList<R> resultList = new ArrayList<R>();
 
         final boolean debug = localLOGV ||
@@ -431,16 +437,21 @@
         final String scheme = intent.getScheme();
         int N = listCut.size();
         for (int i = 0; i < N; ++i) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme,
-                    listCut.get(i), resultList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme,
+                    listCut.get(i), resultList, userId, customFlags);
         }
         filterResults(resultList);
         sortResults(resultList);
         return resultList;
     }
 
-    public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
-            int userId) {
+    public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+            String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+        return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0);
+    }
+
+    protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+            String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
         String scheme = intent.getScheme();
 
         ArrayList<R> finalList = new ArrayList<R>();
@@ -512,21 +523,22 @@
         }
 
         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+        Computer computer = (Computer) snapshot;
         if (firstTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, firstTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, firstTypeCut, finalList, userId, customFlags);
         }
         if (secondTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, secondTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, secondTypeCut, finalList, userId, customFlags);
         }
         if (thirdTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, thirdTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, thirdTypeCut, finalList, userId, customFlags);
         }
         if (schemeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, schemeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, schemeCut, finalList, userId, customFlags);
         }
         filterResults(finalList);
         sortResults(finalList);
@@ -554,7 +566,7 @@
      * "stopped", that is whether it should not be included in the result
      * if the intent requests to excluded stopped objects.
      */
-    protected boolean isFilterStopped(F filter, int userId) {
+    protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) {
         return false;
     }
 
@@ -584,7 +596,8 @@
     protected abstract F[] newArray(int size);
 
     @SuppressWarnings("unchecked")
-    protected R newResult(F filter, int match, int userId) {
+    protected R newResult(@NonNull Computer computer, F filter, int match, int userId,
+            long customFlags) {
         return (R)filter;
     }
 
@@ -764,9 +777,10 @@
         return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
     }
 
-    private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
-            boolean debug, boolean defaultOnly, String resolvedType, String scheme,
-            F[] src, List<R> dest, int userId) {
+    private void buildResolveList(@NonNull Computer computer, Intent intent,
+            FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly,
+            String resolvedType, String scheme, F[] src, List<R> dest, int userId,
+            long customFlags) {
         final String action = intent.getAction();
         final Uri data = intent.getData();
         final String packageName = intent.getPackage();
@@ -791,7 +805,8 @@
             int match;
             if (debug) Slog.v(TAG, "Matching against filter " + filter);
 
-            if (excludingStopped && isFilterStopped(filter, userId)) {
+            if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),
+                    userId)) {
                 if (debug) {
                     Slog.v(TAG, "  Filter's target is stopped; skipping");
                 }
@@ -833,7 +848,7 @@
                         Integer.toHexString(match) + " hasDefault="
                         + intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
                 if (!defaultOnly || intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) {
-                    final R oneResult = newResult(filter, match, userId);
+                    final R oneResult = newResult(computer, filter, match, userId, customFlags);
                     if (debug) Slog.v(TAG, "    Created result: " + oneResult);
                     if (oneResult != null) {
                         dest.add(oneResult);
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 8e53101..16ff167 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
 import static android.app.ActivityManager.UID_OBSERVER_GONE;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -261,6 +262,37 @@
         }
     }
 
+    /** Returns information about pinned files and sizes for StatsPullAtomService. */
+    public List<PinnedFileStats> dumpDataForStatsd() {
+        List<PinnedFileStats> pinnedFileStats = new ArrayList<>();
+        synchronized (PinnerService.this) {
+            for (PinnedFile pinnedFile : mPinnedFiles) {
+                pinnedFileStats.add(new PinnedFileStats(SYSTEM_UID, pinnedFile));
+            }
+
+            for (int key : mPinnedApps.keySet()) {
+                PinnedApp app = mPinnedApps.get(key);
+                for (PinnedFile pinnedFile : mPinnedApps.get(key).mFiles) {
+                    pinnedFileStats.add(new PinnedFileStats(app.uid, pinnedFile));
+                }
+            }
+        }
+        return pinnedFileStats;
+    }
+
+    /** Wrapper class for statistics for a pinned file. */
+    public static class PinnedFileStats {
+        public final int uid;
+        public final String filename;
+        public final int sizeKb;
+
+        protected PinnedFileStats(int uid, PinnedFile file) {
+            this.uid = uid;
+            this.filename = file.fileName.substring(file.fileName.lastIndexOf('/') + 1);
+            this.sizeKb = file.bytesPinned / 1024;
+        }
+    }
+
     /**
      * Handler for on start pinning message
      */
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 811f2f5..efbc4de 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -103,6 +103,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.am.BatteryStatsService;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -2879,6 +2881,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -3016,14 +3019,32 @@
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
         intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+
         // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
-        // for all apps with READ_PRIV but not READ_PHONE_STATE. This ensures that any app holding
-        // either READ_PRIV or READ_PHONE get this broadcast exactly once.
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
-        mContext.createContextAsUser(UserHandle.ALL, 0)
-                .sendBroadcastMultiplePermissions(intent,
-                        new String[] { Manifest.permission.READ_PRIVILEGED_PHONE_STATE },
-                        new String[] { Manifest.permission.READ_PHONE_STATE });
+        // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE.
+        // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with
+        // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION.
+        // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE
+        // get this broadcast exactly once, and we are not exposing location without permission.
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION});
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION},
+                new String[] {Manifest.permission.READ_PHONE_STATE});
+
+        // Replace bundle with location-sanitized ServiceState
+        data = new Bundle();
+        state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data);
+        intent.putExtras(data);
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PHONE_STATE},
+                new String[] {Manifest.permission.ACCESS_FINE_LOCATION});
+        mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+                new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
+                new String[] {Manifest.permission.READ_PHONE_STATE,
+                        Manifest.permission.ACCESS_FINE_LOCATION});
     }
 
     private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index c236a7f..569d480 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -43,6 +43,7 @@
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.UiModeManager;
+import android.app.UiModeManager.NightModeCustomReturnType;
 import android.app.UiModeManager.NightModeCustomType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -111,6 +112,9 @@
     // Enable launching of applications when entering the dock.
     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
     private static final String SYSTEM_PROPERTY_DEVICE_THEME = "persist.sys.theme";
+    @VisibleForTesting
+    public static final Set<Integer> SUPPORTED_NIGHT_MODE_CUSTOM_TYPES = new ArraySet(
+            new Integer[]{MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, MODE_NIGHT_CUSTOM_TYPE_BEDTIME});
 
     private final Injector mInjector;
     private final Object mLock = new Object();
@@ -631,6 +635,8 @@
                         + "permission ENTER_CAR_MODE_PRIORITIZED");
             }
 
+            assertLegit(callingPackage);
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -671,6 +677,9 @@
             // mode flag to be specified; this is so that the user can disable car mode at all
             // priorities using the persistent notification.
             boolean isSystemCaller = mInjector.getCallingUid() == Process.SYSTEM_UID;
+            if (!isSystemCaller) {
+                assertLegit(callingPackage);
+            }
             final int carModeFlags =
                     isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES;
 
@@ -728,8 +737,13 @@
                 case UiModeManager.MODE_NIGHT_NO:
                 case UiModeManager.MODE_NIGHT_YES:
                 case MODE_NIGHT_AUTO:
-                case MODE_NIGHT_CUSTOM:
                     break;
+                case MODE_NIGHT_CUSTOM:
+                    if (SUPPORTED_NIGHT_MODE_CUSTOM_TYPES.contains(customModeType)) {
+                        break;
+                    }
+                    throw new IllegalArgumentException(
+                            "Can't set the custom type to " + customModeType);
                 default:
                     throw new IllegalArgumentException("Unknown mode: " + mode);
             }
@@ -783,7 +797,7 @@
         }
 
         @Override
-        public int getNightModeCustomType() {
+        public  @NightModeCustomReturnType int getNightModeCustomType() {
             if (getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
                     != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e2e53cb..5fe8719 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -46,6 +46,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
@@ -1430,6 +1432,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     void dump(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
                 + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e56098c..8c04c1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -188,6 +189,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.PendingIntentStats;
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
 import android.app.SyncNotedAppOp;
@@ -394,12 +396,14 @@
 import com.android.server.graphics.fonts.FontManagerInternal;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.os.NativeTombstoneManager;
+import com.android.server.pm.Computer;
 import com.android.server.pm.Installer;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.SELinuxUtil;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.uri.GrantUri;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -414,6 +418,7 @@
 import com.android.server.wm.WindowManagerService;
 import com.android.server.wm.WindowProcessController;
 
+import dalvik.annotation.optimization.NeverCompile;
 import dalvik.system.VMRuntime;
 
 import libcore.util.EmptyArray;
@@ -689,12 +694,6 @@
         return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
     }
 
-    /**
-     * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
-     * <p>Not actually used</p>
-     */
-    private volatile String mDeviceOwnerName;
-
     private volatile int mDeviceOwnerUid = INVALID_UID;
 
     /**
@@ -1103,10 +1102,11 @@
         }
 
         @Override
-        protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+        protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
+                int match, int userId, long customFlags) {
             if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
                     || userId == filter.owningUserId) {
-                return super.newResult(filter, match, userId);
+                return super.newResult(computer, filter, match, userId, customFlags);
             }
             return null;
         }
@@ -5614,15 +5614,29 @@
 
         synchronized (mProcLock) {
             synchronized (mPidsSelfLocked) {
+                int newestTimeIndex = -1;
+                long newestTime = Long.MIN_VALUE;
                 for (int i = 0; i < pids.length; i++) {
                     ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
                     if (pr != null) {
-                        final boolean isPendingTop =
-                                mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]);
-                        states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.mState.getCurProcState();
-                        if (scores != null) {
-                            scores[i] = isPendingTop
-                                    ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.mState.getCurAdj();
+                        final long pendingTopTime =
+                                mPendingStartActivityUids.getPendingTopPidTime(pr.uid, pids[i]);
+                        if (pendingTopTime != PendingStartActivityUids.INVALID_TIME) {
+                            // The uid in mPendingStartActivityUids gets the TOP process state.
+                            states[i] = PROCESS_STATE_TOP;
+                            if (scores != null) {
+                                // The uid in mPendingStartActivityUids gets a better score.
+                                scores[i] = ProcessList.FOREGROUND_APP_ADJ - 1;
+                            }
+                            if (pendingTopTime > newestTime) {
+                                newestTimeIndex = i;
+                                newestTime = pendingTopTime;
+                            }
+                        } else {
+                            states[i] = pr.mState.getCurProcState();
+                            if (scores != null) {
+                                scores[i] = pr.mState.getCurAdj();
+                            }
                         }
                     } else {
                         states[i] = PROCESS_STATE_NONEXISTENT;
@@ -5631,6 +5645,13 @@
                         }
                     }
                 }
+                // The uid with the newest timestamp in mPendingStartActivityUids gets the best
+                // score.
+                if (newestTimeIndex != -1) {
+                    if (scores != null) {
+                        scores[newestTimeIndex] = ProcessList.FOREGROUND_APP_ADJ - 2;
+                    }
+                }
             }
         }
     }
@@ -6211,17 +6232,6 @@
     }
 
     @Override
-    public void updateDeviceOwner(String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != SYSTEM_UID) {
-            throw new SecurityException("updateDeviceOwner called from non-system process");
-        }
-        synchronized (this) {
-            mDeviceOwnerName = packageName;
-        }
-    }
-
-    @Override
     public void updateLockTaskPackages(int userId, String[] packages) {
         mActivityTaskManager.updateLockTaskPackages(userId, packages);
     }
@@ -6658,6 +6668,7 @@
                 reportCurWakefulnessUsageEvent();
                 mActivityTaskManager.onScreenAwakeChanged(isAwake);
                 mOomAdjProfiler.onWakefulnessChanged(wakefulness);
+                mOomAdjuster.onWakefulnessChanged(wakefulness);
             }
             updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
         }
@@ -9153,6 +9164,7 @@
     /**
      * Wrapper function to print out debug data filtered by specified arguments.
     */
+    @NeverCompile // Avoid size overhead of debugging code.
     private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
 
@@ -9687,6 +9699,7 @@
         return needSep;
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @GuardedBy({"this", "mProcLock"})
     void dumpOtherProcessesInfoLSP(FileDescriptor fd, PrintWriter pw,
             boolean dumpAll, String dumpPackage, int dumpAppId, int numPers, boolean needSep) {
@@ -10808,6 +10821,7 @@
         boolean dumpProto;
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
             String[] args, boolean brief, PrintWriter categoryPw, boolean asProto) {
         MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions();
@@ -10890,6 +10904,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix,
             MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
@@ -11539,6 +11554,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private final void dumpApplicationMemoryUsage(FileDescriptor fd,
             MemoryUsageDumpOptions opts, final String[] innerArgs, boolean brief,
             ArrayList<ProcessRecord> procs) {
@@ -13010,7 +13026,7 @@
                 if (!bf.debugCheck()) {
                     Slog.w(TAG, "==> For Dynamic broadcast");
                 }
-                mReceiverResolver.addFilter(bf);
+                mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
             }
 
             // Enqueue broadcasts for all existing stickies that match
@@ -13563,6 +13579,8 @@
                                                 intent.getIntExtra(Intent.EXTRA_UID, -1)),
                                                 false, true, true, false, fullUninstall, userId,
                                                 removed ? "pkg removed" : "pkg changed");
+                                        getPackageManagerInternal()
+                                                .onPackageProcessKilledForUninstall(ssp);
                                     } else {
                                         // Kill any app zygotes always, since they can't fork new
                                         // processes with references to the old code
@@ -13832,6 +13850,7 @@
                     intent, resolvedType, callingUid, users, broadcastAllowList);
         }
         if (intent.getComponent() == null) {
+            final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
             if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                 // Query one target user at a time, excluding shell-restricted users
                 for (int i = 0; i < users.length; i++) {
@@ -13840,7 +13859,7 @@
                         continue;
                     }
                     List<BroadcastFilter> registeredReceiversForUser =
-                            mReceiverResolver.queryIntent(intent,
+                            mReceiverResolver.queryIntent(snapshot, intent,
                                     resolvedType, false /*defaultOnly*/, users[i]);
                     if (registeredReceivers == null) {
                         registeredReceivers = registeredReceiversForUser;
@@ -13849,7 +13868,7 @@
                     }
                 }
             } else {
-                registeredReceivers = mReceiverResolver.queryIntent(intent,
+                registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
                         resolvedType, false /*defaultOnly*/, userId);
             }
         }
@@ -14310,14 +14329,18 @@
                 return false;
             }
 
-            if (!Build.IS_DEBUGGABLE) {
-                int match = mContext.getPackageManager().checkSignatures(
-                        ii.targetPackage, ii.packageName);
-                if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+            int match = mContext.getPackageManager().checkSignatures(
+                    ii.targetPackage, ii.packageName);
+            if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+                if (Build.IS_DEBUGGABLE && (flags & INSTR_FLAG_ALWAYS_CHECK_SIGNATURE) == 0) {
+                    Slog.w(TAG, "Instrumentation test " + ii.packageName
+                            + " doesn't have a signature matching the target " + ii.targetPackage
+                            + ", which would not be allowed on the production Android builds");
+                } else {
                     String msg = "Permission Denial: starting instrumentation "
                             + className + " from pid="
                             + Binder.getCallingPid()
-                            + ", uid=" + Binder.getCallingPid()
+                            + ", uid=" + Binder.getCallingUid()
                             + " not allowed because package " + ii.packageName
                             + " does not have a signature matching the target "
                             + ii.targetPackage;
@@ -15585,24 +15608,20 @@
         }
         for (int i = 0, size = processes.size(); i < size; i++) {
             ProcessRecord app = processes.get(i);
-            pw.println(String.format("------ DUMP RESOURCES %s (%s)  ------",
+            pw.println(String.format("Resources History for %s (%s)",
                     app.processName,
                     app.info.packageName));
             pw.flush();
             try {
-                TransferPipe tp = new TransferPipe();
+                TransferPipe tp = new TransferPipe("  ");
                 try {
                     IApplicationThread thread = app.getThread();
                     if (thread != null) {
                         app.getThread().dumpResources(tp.getWriteFd(), null);
                         tp.go(fd.getFileDescriptor(), 2000);
-                        pw.println(String.format("------ END DUMP RESOURCES %s (%s)  ------",
-                                app.processName,
-                                app.info.packageName));
-                        pw.flush();
                     } else {
                         pw.println(String.format(
-                                "------ DUMP RESOURCES %s (%s) failed, no thread ------",
+                                "  Resources history for %s (%s) failed, no thread",
                                 app.processName,
                                 app.info.packageName));
                     }
@@ -15610,11 +15629,7 @@
                     tp.kill();
                 }
             } catch (IOException e) {
-                pw.println(String.format(
-                        "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------",
-                        app.processName,
-                        app.info.packageName,
-                        e.getMessage()));
+                pw.println("  " + e.getMessage());
                 pw.flush();
             }
 
@@ -15952,6 +15967,11 @@
             implements ActivityManagerLocal {
 
         @Override
+        public List<PendingIntentStats> getPendingIntentStats() {
+            return mPendingIntentController.dumpPendingIntentStatsForStatsd();
+        }
+
+        @Override
         public Pair<String, String> getAppProfileStatsForDebugging(long time, int lines) {
             return mAppProfiler.getAppProfileStatsForDebugging(time, lines);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 08508b2..28b807c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -34,6 +34,7 @@
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
 import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -107,6 +108,8 @@
 import com.android.server.compat.PlatformCompat;
 import com.android.server.utils.Slogf;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -3309,6 +3312,7 @@
         dumpHelp(pw, mDumping);
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     static void dumpHelp(PrintWriter pw, boolean dumping) {
         if (dumping) {
             pw.println("Activity manager dump options:");
@@ -3346,7 +3350,11 @@
             pw.println("  --checkin: output checkin format, resetting data.");
             pw.println("  --C: output checkin format, not resetting data.");
             pw.println("  --proto: output dump in protocol buffer format.");
-            pw.println("  --autofill: dump just the autofill-related state of an activity");
+            pw.printf("  %s: dump just the DUMPABLE-related state of an activity. Use the %s "
+                    + "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
+                    Activity.DUMP_ARG_LIST_DUMPABLES);
+            pw.printf("  %s: show the available dumpables in an activity\n",
+                    Activity.DUMP_ARG_LIST_DUMPABLES);
         } else {
             pw.println("Activity manager (activity) commands:");
             pw.println("  help");
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index 34d9a60..1e1024e 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -60,6 +60,10 @@
      */
     private long mLastAnrTimeMs = 0L;
 
+    /** The pid which is running appNotResponding(). */
+    @GuardedBy("mAnrRecords")
+    private int mProcessingPid = -1;
+
     AnrHelper(final ActivityManagerService service) {
         mService = service;
     }
@@ -73,7 +77,18 @@
     void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
             ApplicationInfo aInfo, String parentShortComponentName,
             WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
+        final int incomingPid = anrProcess.mPid;
         synchronized (mAnrRecords) {
+            if (mProcessingPid == incomingPid) {
+                Slog.i(TAG, "Skip duplicated ANR, pid=" + incomingPid + " " + annotation);
+                return;
+            }
+            for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
+                if (mAnrRecords.get(i).mPid == incomingPid) {
+                    Slog.i(TAG, "Skip queued ANR, pid=" + incomingPid + " " + annotation);
+                    return;
+                }
+            }
             mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                     parentShortComponentName, parentProcess, aboveSystem, annotation));
         }
@@ -87,8 +102,8 @@
     }
 
     /**
-     * The thread to execute {@link ProcessRecord#appNotResponding}. It will terminate if all
-     * records are handled.
+     * The thread to execute {@link ProcessErrorStateRecord#appNotResponding}. It will terminate if
+     * all records are handled.
      */
     private class AnrConsumerThread extends Thread {
         AnrConsumerThread() {
@@ -97,7 +112,12 @@
 
         private AnrRecord next() {
             synchronized (mAnrRecords) {
-                return mAnrRecords.isEmpty() ? null : mAnrRecords.remove(0);
+                if (mAnrRecords.isEmpty()) {
+                    return null;
+                }
+                final AnrRecord record = mAnrRecords.remove(0);
+                mProcessingPid = record.mPid;
+                return record;
             }
         }
 
@@ -106,6 +126,13 @@
             AnrRecord r;
             while ((r = next()) != null) {
                 scheduleBinderHeavyHitterAutoSamplerIfNecessary();
+                final int currentPid = r.mApp.mPid;
+                if (currentPid != r.mPid) {
+                    // The process may have restarted or died.
+                    Slog.i(TAG, "Skip ANR with mismatched pid=" + r.mPid + ", current pid="
+                            + currentPid);
+                    continue;
+                }
                 final long startTime = SystemClock.uptimeMillis();
                 // If there are many ANR at the same time, the latency may be larger. If the latency
                 // is too large, the stack trace might not be meaningful.
@@ -120,6 +147,7 @@
 
             mRunning.set(false);
             synchronized (mAnrRecords) {
+                mProcessingPid = -1;
                 // The race should be unlikely to happen. Just to make sure we don't miss.
                 if (!mAnrRecords.isEmpty()) {
                     startAnrConsumerIfNeeded();
@@ -139,6 +167,7 @@
 
     private static class AnrRecord {
         final ProcessRecord mApp;
+        final int mPid;
         final String mActivityShortComponentName;
         final String mParentShortComponentName;
         final String mAnnotation;
@@ -151,6 +180,7 @@
                 ApplicationInfo aInfo, String parentShortComponentName,
                 WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
             mApp = anrProcess;
+            mPid = anrProcess.mPid;
             mActivityShortComponentName = activityShortComponentName;
             mParentShortComponentName = parentShortComponentName;
             mAnnotation = annotation;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6c1a00d..97dd323 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -1018,6 +1018,7 @@
                         Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                         0,
                         mService.mUserController.getCurrentUserId()) != 0;
+                final String packageName = proc.info.packageName;
                 final boolean crashSilenced = mAppsNotReportingCrashes != null
                         && mAppsNotReportingCrashes.contains(proc.info.packageName);
                 final long now = SystemClock.uptimeMillis();
@@ -1026,6 +1027,7 @@
                 if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                         && !crashSilenced && !shouldThottle
                         && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
+                    Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
                     errState.getDialogController().showCrashDialogs(data);
                     if (!proc.isolated) {
                         mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index 5ac5a70..075402f 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -560,20 +560,22 @@
             int changes = serviceTypes ^ mForegroundServiceTypes;
             for (int serviceType = Integer.highestOneBit(changes); serviceType != 0;) {
                 final int i = foregroundServiceTypeToIndex(serviceType);
-                if ((serviceTypes & serviceType) != 0) {
-                    // Start this type.
-                    if (mEvents[i] == null) {
-                        mEvents[i] = new LinkedList<>();
-                    }
-                    if (!isActive(i)) {
-                        mEvents[i].add(new BaseTimeEvent(now));
-                        notifyListenersOnStateChangeIfNecessary(true, now, serviceType);
-                    }
-                } else {
-                    // Stop this type.
-                    if (mEvents[i] != null && isActive(i)) {
-                        mEvents[i].add(new BaseTimeEvent(now));
-                        notifyListenersOnStateChangeIfNecessary(false, now, serviceType);
+                if (i < mEvents.length) {
+                    if ((serviceTypes & serviceType) != 0) {
+                        // Start this type.
+                        if (mEvents[i] == null) {
+                            mEvents[i] = new LinkedList<>();
+                        }
+                        if (!isActive(i)) {
+                            mEvents[i].add(new BaseTimeEvent(now));
+                            notifyListenersOnStateChangeIfNecessary(true, now, serviceType);
+                        }
+                    } else {
+                        // Stop this type.
+                        if (mEvents[i] != null && isActive(i)) {
+                            mEvents[i].add(new BaseTimeEvent(now));
+                            notifyListenersOnStateChangeIfNecessary(false, now, serviceType);
+                        }
                     }
                 }
                 changes &= ~serviceType;
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 7f48d52..69f70ca 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -64,6 +64,8 @@
     @GuardedBy("mLock")
     private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
 
+    private volatile boolean mLockedBootCompleted = false;
+
     AppPermissionTracker(Context context, AppRestrictionController controller) {
         this(context, controller, null, null);
     }
@@ -85,20 +87,20 @@
         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
         final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
         final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+        final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
         for (int userId : allUsers) {
             final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
             if (apps == null) {
                 continue;
             }
-            synchronized (mLock) {
-                final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
-                final long now = SystemClock.elapsedRealtime();
-                for (int i = 0, size = apps.size(); i < size; i++) {
-                    final ApplicationInfo ai = apps.get(i);
-                    for (String permission : permissions) {
-                        if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
-                            continue;
-                        }
+            final long now = SystemClock.elapsedRealtime();
+            for (int i = 0, size = apps.size(); i < size; i++) {
+                final ApplicationInfo ai = apps.get(i);
+                for (String permission : permissions) {
+                    if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+                        continue;
+                    }
+                    synchronized (mLock) {
                         ArraySet<String> grantedPermissions = uidPerms.get(ai.uid);
                         if (grantedPermissions == null) {
                             grantedPermissions = new ArraySet<String>();
@@ -132,25 +134,30 @@
     private void handlePermissionsChanged(int uid) {
         final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
         if (permissions != null && permissions.length > 0) {
+            final PermissionManagerServiceInternal pm =
+                    mInjector.getPermissionManagerServiceInternal();
+            final boolean[] states = new boolean[permissions.length];
+            for (int i = 0; i < permissions.length; i++) {
+                states[i] = pm.checkUidPermission(uid, permissions[i]) == PERMISSION_GRANTED;
+                if (DEBUG_PERMISSION_TRACKER) {
+                    Slog.i(TAG, UserHandle.formatUid(uid) + " " + permissions[i] + "=" + states[i]);
+                }
+            }
             synchronized (mLock) {
-                handlePermissionsChangedLocked(uid);
+                handlePermissionsChangedLocked(uid, permissions, states);
             }
         }
     }
 
     @GuardedBy("mLock")
-    private void handlePermissionsChangedLocked(int uid) {
-        final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+    private void handlePermissionsChangedLocked(int uid, String[] permissions, boolean[] states) {
         final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
         ArraySet<String> grantedPermissions = index >= 0
                 ? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
-        final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
         final long now = SystemClock.elapsedRealtime();
-        for (String permission: permissions) {
-            boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
-            if (DEBUG_PERMISSION_TRACKER) {
-                Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
-            }
+        for (int i = 0; i < permissions.length; i++) {
+            final String permission = permissions[i];
+            final boolean granted = states[i];
             boolean changed = false;
             if (granted) {
                 if (grantedPermissions == null) {
@@ -200,6 +207,10 @@
     }
 
     private void onPermissionTrackerEnabled(boolean enabled) {
+        if (!mLockedBootCompleted) {
+            // Not ready, bail out.
+            return;
+        }
         final PermissionManager pm = mInjector.getPermissionManager();
         if (enabled) {
             pm.addOnPermissionsChangeListener(this);
@@ -211,6 +222,12 @@
     }
 
     @Override
+    void onLockedBootCompleted() {
+        mLockedBootCompleted = true;
+        onPermissionTrackerEnabled(mInjector.getPolicy().isEnabled());
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
         pw.println("APP PERMISSIONS TRACKER:");
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 1129c19..2ffd487 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -71,6 +71,7 @@
 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
 import static android.os.PowerExemptionManager.reasonCodeToString;
 import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -792,7 +793,7 @@
         mInjector = injector;
         mContext = injector.getContext();
         mActivityManagerService = service;
-        mBgHandlerThread = new HandlerThread("bgres-controller");
+        mBgHandlerThread = new HandlerThread("bgres-controller", THREAD_PRIORITY_BACKGROUND);
         mBgHandlerThread.start();
         mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
         mBgExecutor = new HandlerExecutor(mBgHandler);
@@ -816,9 +817,11 @@
         mInjector.getAppStandbyInternal().addListener(mAppIdleStateChangeListener);
         mInjector.getRoleManager().addOnRoleHoldersChangedListenerAsUser(mBgExecutor,
                 mRoleHolderChangedListener, UserHandle.ALL);
-        for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
-            mAppStateTrackers.get(i).onSystemReady();
-        }
+        mInjector.scheduleInitTrackers(mBgHandler, () -> {
+            for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+                mAppStateTrackers.get(i).onSystemReady();
+            }
+        });
     }
 
     @VisibleForTesting
@@ -2137,6 +2140,10 @@
             }
             return null;
         }
+
+        void scheduleInitTrackers(Handler handler, Runnable initializers) {
+            handler.post(initializers);
+        }
     }
 
     private void registerForSystemBroadcasts() {
@@ -2221,6 +2228,21 @@
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         userFilter.addAction(Intent.ACTION_UID_REMOVED);
         mContext.registerReceiverForAllUsers(broadcastReceiver, userFilter, null, mBgHandler);
+        final BroadcastReceiver bootReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                switch (intent.getAction()) {
+                    case Intent.ACTION_LOCKED_BOOT_COMPLETED: {
+                        onLockedBootCompleted();
+                    } break;
+                }
+            }
+        };
+        final IntentFilter bootFilter = new IntentFilter();
+        bootFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+        mContext.registerReceiverAsUser(bootReceiver, UserHandle.SYSTEM,
+                bootFilter, null, mBgHandler);
     }
 
     void forEachTracker(Consumer<BaseAppStateTracker> sink) {
@@ -2275,6 +2297,12 @@
         mRestrictionSettings.removeUid(uid);
     }
 
+    private void onLockedBootCompleted() {
+        for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+            mAppStateTrackers.get(i).onLockedBootCompleted();
+        }
+    }
+
     boolean isBgAutoRestrictedBucketFeatureFlagEnabled() {
         return mConstantsObserver.mBgAutoRestrictedBucket;
     }
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 482d697..0fada53 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -204,6 +204,12 @@
     }
 
     /**
+     * Called when the system sends LOCKED_BOOT_COMPLETED.
+     */
+    void onLockedBootCompleted() {
+    }
+
+    /**
      * Called when a device config property in the activity manager namespace
      * has changed.
      */
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 7af73eb..86ca699 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -28,6 +28,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -1051,6 +1052,15 @@
         }
     }
 
+    void onWakefulnessChanged(int wakefulness) {
+        if(wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
+            // Remove any pending compaction we may have scheduled to happen while screen was off
+            Slog.e(TAG_AM, "Cancel pending or running compactions as system is awake");
+            mPendingCompactionProcesses.clear();
+            cancelCompaction();
+        }
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
         // Cancel any currently executing compactions
@@ -1105,6 +1115,9 @@
                     int lastOomAdj = msg.arg1;
                     int procState = msg.arg2;
                     synchronized (mProcLock) {
+                        if(mPendingCompactionProcesses.isEmpty()) {
+                            return;
+                        }
                         proc = mPendingCompactionProcesses.remove(0);
                         opt = proc.mOptRecord;
 
diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
index f0910dc..4c96078 100644
--- a/services/core/java/com/android/server/am/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -73,7 +73,8 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
-        mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler);
+        mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler,
+                Context.RECEIVER_NOT_EXPORTED);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 6c9187a..8d77eda 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2470,6 +2470,10 @@
         }
     }
 
+    void onWakefulnessChanged(int wakefulness) {
+        mCachedAppOptimizer.onWakefulnessChanged(wakefulness);
+    }
+
     /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
     @GuardedBy({"mService", "mProcLock"})
     private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index c49e696..7ee96aa 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.PendingIntent;
+import android.app.PendingIntentStats;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.os.Binder;
@@ -60,6 +61,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * Helper class for {@link ActivityManagerService} responsible for managing pending intents.
@@ -422,6 +424,55 @@
     }
 
     /**
+     * Provides some stats tracking of the current state of the PendingIntent queue.
+     *
+     * Data about the pending intent queue is intended to be used for memory impact tracking.
+     * Returned data (one per uid) will consist of instances of PendingIntentStats containing
+     * (I) number of PendingIntents and (II) total size of all bundled extras in the PIs.
+     *
+     * @hide
+     */
+    public List<PendingIntentStats> dumpPendingIntentStatsForStatsd() {
+        List<PendingIntentStats> pendingIntentStats = new ArrayList<>();
+
+        synchronized (mLock) {
+            if (mIntentSenderRecords.size() > 0) {
+                // First, aggregate PendingIntent data by package uid.
+                final SparseIntArray countsByUid = new SparseIntArray();
+                final SparseIntArray bundleSizesByUid = new SparseIntArray();
+
+                for (WeakReference<PendingIntentRecord> reference : mIntentSenderRecords.values()) {
+                    if (reference == null || reference.get() == null) {
+                        continue;
+                    }
+                    PendingIntentRecord record = reference.get();
+                    int index = countsByUid.indexOfKey(record.uid);
+
+                    if (index < 0) { // ie. the key was not found
+                        countsByUid.put(record.uid, 1);
+                        bundleSizesByUid.put(record.uid,
+                                record.key.requestIntent.getExtrasTotalSize());
+                    } else {
+                        countsByUid.put(record.uid, countsByUid.valueAt(index) + 1);
+                        bundleSizesByUid.put(record.uid,
+                                bundleSizesByUid.valueAt(index)
+                                + record.key.requestIntent.getExtrasTotalSize());
+                    }
+                }
+
+                // Now generate the output.
+                for (int i = 0, size = countsByUid.size(); i < size; i++) {
+                    pendingIntentStats.add(new PendingIntentStats(
+                            countsByUid.keyAt(i),
+                            countsByUid.valueAt(i),
+                            /* NB: int conversion here */ bundleSizesByUid.valueAt(i) / 1024));
+                }
+            }
+        }
+        return pendingIntentStats;
+    }
+
+    /**
      * Increment the number of the PendingIntentRecord for the given uid, log a warning
      * if there are too many for this uid already.
      */
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index 6bf9d4e..802ea00 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -35,6 +35,8 @@
 final class PendingStartActivityUids {
     static final String TAG = ActivityManagerService.TAG;
 
+    public static final long INVALID_TIME = 0;
+
     // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the
     // uid is added.
     private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray();
@@ -63,13 +65,20 @@
         }
     }
 
-    synchronized boolean isPendingTopPid(int uid, int pid) {
+    /**
+     * Return the elapsedRealtime when the uid is added to the mPendingUids map.
+     * @param uid
+     * @param pid
+     * @return elapsedRealtime if the uid is in the mPendingUids map;
+     *         INVALID_TIME if the uid is not in the mPendingUids map.
+     */
+    synchronized long getPendingTopPidTime(int uid, int pid) {
+        long ret = INVALID_TIME;
         final Pair<Integer, Long> pendingPid = mPendingUids.get(uid);
-        if (pendingPid != null) {
-            return pendingPid.first == pid;
-        } else {
-            return false;
+        if (pendingPid != null && pendingPid.first == pid) {
+            ret = pendingPid.second;
         }
+        return ret;
     }
 
     synchronized boolean isPendingTopUid(int uid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c4163e6..763bbee 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1718,17 +1718,18 @@
             return Zygote.MEMORY_TAG_LEVEL_NONE;
         }
 
-        // Check to see that the compat feature for TBI is enabled.
-        if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
-            return Zygote.MEMORY_TAG_LEVEL_TBI;
-        }
-
         String defaultLevel = SystemProperties.get("persist.arm64.memtag.app_default");
         if ("sync".equals(defaultLevel)) {
             return Zygote.MEMORY_TAG_LEVEL_SYNC;
         } else if ("async".equals(defaultLevel)) {
             return Zygote.MEMORY_TAG_LEVEL_ASYNC;
         }
+
+        // Check to see that the compat feature for TBI is enabled.
+        if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+            return Zygote.MEMORY_TAG_LEVEL_TBI;
+        }
+
         return Zygote.MEMORY_TAG_LEVEL_NONE;
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 84d2b1f..7371d07 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -44,6 +44,8 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -932,6 +934,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private void dumpInner(PrintWriter pw, String[] args) {
         final long now = SystemClock.uptimeMillis();
 
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 9ba9d78..0edbea0 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -90,6 +90,7 @@
 import com.android.internal.compat.CompatibilityOverrideConfig;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
@@ -159,7 +160,7 @@
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
             mGameServiceController = new GameServiceController(
-                    BackgroundThread.getExecutor(),
+                    context, BackgroundThread.getExecutor(),
                     new GameServiceProviderSelectorImpl(
                             context.getResources(),
                             context.getPackageManager()),
@@ -268,16 +269,34 @@
                     break;
                 }
                 case SET_GAME_STATE: {
-                    if (mPowerManagerInternal == null) {
-                        final Bundle data = msg.getData();
-                        Slog.d(TAG, "Error setting loading mode for package "
-                                + data.getString(PACKAGE_NAME_MSG_KEY)
-                                + " and userId " + data.getInt(USER_ID_MSG_KEY));
-                        break;
-                    }
                     final GameState gameState = (GameState) msg.obj;
                     final boolean isLoading = gameState.isLoading();
-                    mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+                    final Bundle data = msg.getData();
+                    final String packageName = data.getString(PACKAGE_NAME_MSG_KEY);
+                    final int userId = data.getInt(USER_ID_MSG_KEY);
+
+                    // Restrict to games only. Requires performance mode to be enabled.
+                    final boolean boostEnabled =
+                            getGameMode(packageName, userId) == GameManager.GAME_MODE_PERFORMANCE;
+                    int uid;
+                    try {
+                        uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+                    } catch (NameNotFoundException e) {
+                        Slog.v(TAG, "Failed to get package metadata");
+                        uid = -1;
+                    }
+                    FrameworkStatsLog.write(FrameworkStatsLog.GAME_STATE_CHANGED, packageName, uid,
+                            boostEnabled, gameStateModeToStatsdGameState(gameState.getMode()),
+                            isLoading, gameState.getLabel(), gameState.getQuality());
+
+                    if (boostEnabled) {
+                        if (mPowerManagerInternal == null) {
+                            Slog.d(TAG, "Error setting loading mode for package " + packageName
+                                    + " and userId " + userId);
+                            break;
+                        }
+                        mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+                    }
                     break;
                 }
             }
@@ -376,9 +395,10 @@
 
     /**
      * Called by games to communicate the current state to the platform.
+     *
      * @param packageName The client package name.
-     * @param gameState An object set to the current state.
-     * @param userId The user associated with this state.
+     * @param gameState   An object set to the current state.
+     * @param userId      The user associated with this state.
      */
     public void setGameState(String packageName, @NonNull GameState gameState,
             @UserIdInt int userId) {
@@ -386,12 +406,6 @@
             // Restrict to games only.
             return;
         }
-
-        if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) {
-            // Requires performance mode to be enabled.
-            return;
-        }
-
         final Message msg = mHandler.obtainMessage(SET_GAME_STATE);
         final Bundle data = new Bundle();
         data.putString(PACKAGE_NAME_MSG_KEY, packageName);
@@ -1373,7 +1387,7 @@
      * @hide
      */
     @VisibleForTesting
-    void updateConfigsForUser(@UserIdInt int userId, String ...packageNames) {
+    void updateConfigsForUser(@UserIdInt int userId, String... packageNames) {
         try {
             synchronized (mDeviceConfigLock) {
                 for (final String packageName : packageNames) {
@@ -1442,7 +1456,7 @@
         final List<PackageInfo> packages =
                 mPackageManager.getInstalledPackagesAsUser(0, userId);
         return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
-                == ApplicationInfo.CATEGORY_GAME)
+                        == ApplicationInfo.CATEGORY_GAME)
                 .map(e -> e.packageName)
                 .toArray(String[]::new);
     }
@@ -1542,6 +1556,22 @@
         return out.toString();
     }
 
+    private static int gameStateModeToStatsdGameState(int mode) {
+        switch (mode) {
+            case GameState.MODE_NONE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_NONE;
+            case GameState.MODE_GAMEPLAY_INTERRUPTIBLE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_INTERRUPTIBLE;
+            case GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_UNINTERRUPTIBLE;
+            case GameState.MODE_CONTENT:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_CONTENT;
+            case GameState.MODE_UNKNOWN:
+            default:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_UNKNOWN;
+        }
+    }
+
     private static ServiceThread createServiceThread() {
         ServiceThread handlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/app/GameServiceConfiguration.java b/services/core/java/com/android/server/app/GameServiceConfiguration.java
new file mode 100644
index 0000000..1f31a87
--- /dev/null
+++ b/services/core/java/com/android/server/app/GameServiceConfiguration.java
@@ -0,0 +1,154 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Representation of a {@link android.service.games.GameService} provider configuration.
+ */
+final class GameServiceConfiguration {
+    private final String mPackageName;
+    @Nullable
+    private final GameServiceComponentConfiguration mGameServiceComponentConfiguration;
+
+    GameServiceConfiguration(
+            @NonNull String packageName,
+            @Nullable GameServiceComponentConfiguration gameServiceComponentConfiguration) {
+        Objects.requireNonNull(packageName);
+
+        mPackageName = packageName;
+        mGameServiceComponentConfiguration = gameServiceComponentConfiguration;
+    }
+
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Nullable
+    public GameServiceComponentConfiguration getGameServiceComponentConfiguration() {
+        return mGameServiceComponentConfiguration;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GameServiceConfiguration)) {
+            return false;
+        }
+
+        GameServiceConfiguration that = (GameServiceConfiguration) o;
+        return TextUtils.equals(mPackageName, that.mPackageName)
+                && Objects.equals(mGameServiceComponentConfiguration,
+                that.mGameServiceComponentConfiguration);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName, mGameServiceComponentConfiguration);
+    }
+
+    @Override
+    public String toString() {
+        return "GameServiceConfiguration{"
+                + "packageName="
+                + mPackageName
+                + ", gameServiceComponentConfiguration="
+                + mGameServiceComponentConfiguration
+                + '}';
+    }
+
+    static final class GameServiceComponentConfiguration {
+        private final UserHandle mUserHandle;
+        private final ComponentName mGameServiceComponentName;
+        private final ComponentName mGameSessionServiceComponentName;
+
+        GameServiceComponentConfiguration(
+                @NonNull UserHandle userHandle, @NonNull ComponentName gameServiceComponentName,
+                @NonNull ComponentName gameSessionServiceComponentName) {
+            Objects.requireNonNull(userHandle);
+            Objects.requireNonNull(gameServiceComponentName);
+            Objects.requireNonNull(gameSessionServiceComponentName);
+
+            mUserHandle = userHandle;
+            mGameServiceComponentName = gameServiceComponentName;
+            mGameSessionServiceComponentName = gameSessionServiceComponentName;
+        }
+
+        @NonNull
+        public UserHandle getUserHandle() {
+            return mUserHandle;
+        }
+
+        @NonNull
+        public ComponentName getGameServiceComponentName() {
+            return mGameServiceComponentName;
+        }
+
+        @NonNull
+        public ComponentName getGameSessionServiceComponentName() {
+            return mGameSessionServiceComponentName;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            if (!(o instanceof GameServiceComponentConfiguration)) {
+                return false;
+            }
+
+            GameServiceComponentConfiguration that =
+                    (GameServiceComponentConfiguration) o;
+            return mUserHandle.equals(that.mUserHandle) && mGameServiceComponentName.equals(
+                    that.mGameServiceComponentName)
+                    && mGameSessionServiceComponentName.equals(
+                    that.mGameSessionServiceComponentName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mUserHandle,
+                    mGameServiceComponentName,
+                    mGameSessionServiceComponentName);
+        }
+
+        @Override
+        public String toString() {
+            return "GameServiceComponentConfiguration{"
+                    + "userHandle="
+                    + mUserHandle
+                    + ", gameServiceComponentName="
+                    + mGameServiceComponentName
+                    + ", gameSessionServiceComponentName="
+                    + mGameSessionServiceComponentName
+                    + "}";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/app/GameServiceController.java b/services/core/java/com/android/server/app/GameServiceController.java
index 397439a..db1ca97 100644
--- a/services/core/java/com/android/server/app/GameServiceController.java
+++ b/services/core/java/com/android/server/app/GameServiceController.java
@@ -19,10 +19,17 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
 
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -36,8 +43,8 @@
 final class GameServiceController {
     private static final String TAG = "GameServiceController";
 
-
     private final Object mLock = new Object();
+    private final Context mContext;
     private final Executor mBackgroundExecutor;
     private final GameServiceProviderSelector mGameServiceProviderSelector;
     private final GameServiceProviderInstanceFactory mGameServiceProviderInstanceFactory;
@@ -46,18 +53,24 @@
     @Nullable
     private volatile String mGameServiceProviderOverride;
     @Nullable
+    private BroadcastReceiver mGameServicePackageChangedReceiver;
+    @Nullable
     private volatile SystemService.TargetUser mCurrentForegroundUser;
     @GuardedBy("mLock")
     @Nullable
-    private volatile GameServiceProviderConfiguration mActiveGameServiceProviderConfiguration;
+    private volatile GameServiceComponentConfiguration mActiveGameServiceComponentConfiguration;
     @GuardedBy("mLock")
     @Nullable
     private volatile GameServiceProviderInstance mGameServiceProviderInstance;
+    @GuardedBy("mLock")
+    @Nullable
+    private volatile String mActiveGameServiceProviderPackage;
 
     GameServiceController(
-            @NonNull Executor backgroundExecutor,
+            @NonNull Context context, @NonNull Executor backgroundExecutor,
             @NonNull GameServiceProviderSelector gameServiceProviderSelector,
             @NonNull GameServiceProviderInstanceFactory gameServiceProviderInstanceFactory) {
+        mContext = context;
         mGameServiceProviderInstanceFactory = gameServiceProviderInstanceFactory;
         mBackgroundExecutor = backgroundExecutor;
         mGameServiceProviderSelector = gameServiceProviderSelector;
@@ -139,35 +152,92 @@
         }
 
         synchronized (mLock) {
-            GameServiceProviderConfiguration selectedGameServiceProviderConfiguration =
+            final GameServiceConfiguration selectedGameServiceConfiguration =
                     mGameServiceProviderSelector.get(mCurrentForegroundUser,
                             mGameServiceProviderOverride);
+            final String gameServicePackage =
+                    selectedGameServiceConfiguration == null ? null :
+                            selectedGameServiceConfiguration.getPackageName();
+            final GameServiceComponentConfiguration gameServiceComponentConfiguration =
+                    selectedGameServiceConfiguration == null ? null
+                            : selectedGameServiceConfiguration
+                                    .getGameServiceComponentConfiguration();
 
-            boolean didActiveGameServiceProviderChanged =
-                    !Objects.equals(selectedGameServiceProviderConfiguration,
-                            mActiveGameServiceProviderConfiguration);
-            if (!didActiveGameServiceProviderChanged) {
+            evaluateGameServiceProviderPackageChangedListenerLocked(gameServicePackage);
+
+            boolean didActiveGameServiceProviderChange =
+                    !Objects.equals(gameServiceComponentConfiguration,
+                            mActiveGameServiceComponentConfiguration);
+            if (!didActiveGameServiceProviderChange) {
                 return;
             }
 
             if (mGameServiceProviderInstance != null) {
                 Slog.i(TAG, "Stopping Game Service provider: "
-                        + mActiveGameServiceProviderConfiguration);
+                        + mActiveGameServiceComponentConfiguration);
                 mGameServiceProviderInstance.stop();
+                mGameServiceProviderInstance = null;
             }
 
-            mActiveGameServiceProviderConfiguration = selectedGameServiceProviderConfiguration;
-
-            if (mActiveGameServiceProviderConfiguration == null) {
+            mActiveGameServiceComponentConfiguration = gameServiceComponentConfiguration;
+            if (mActiveGameServiceComponentConfiguration == null) {
                 return;
             }
 
             Slog.i(TAG,
-                    "Starting Game Service provider: " + mActiveGameServiceProviderConfiguration);
+                    "Starting Game Service provider: " + mActiveGameServiceComponentConfiguration);
             mGameServiceProviderInstance =
                     mGameServiceProviderInstanceFactory.create(
-                            mActiveGameServiceProviderConfiguration);
+                            mActiveGameServiceComponentConfiguration);
             mGameServiceProviderInstance.start();
         }
     }
+
+    @GuardedBy("mLock")
+    private void evaluateGameServiceProviderPackageChangedListenerLocked(
+            @Nullable String gameServicePackage) {
+        if (TextUtils.equals(mActiveGameServiceProviderPackage, gameServicePackage)) {
+            return;
+        }
+
+        if (mGameServicePackageChangedReceiver != null) {
+            mContext.unregisterReceiver(mGameServicePackageChangedReceiver);
+            mGameServicePackageChangedReceiver = null;
+        }
+
+        mActiveGameServiceProviderPackage = gameServicePackage;
+
+        if (TextUtils.isEmpty(mActiveGameServiceProviderPackage)) {
+            return;
+        }
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        intentFilter.addDataSchemeSpecificPart(gameServicePackage, PatternMatcher.PATTERN_LITERAL);
+        mGameServicePackageChangedReceiver = new PackageChangedBroadcastReceiver(
+                gameServicePackage);
+        mContext.registerReceiver(
+                mGameServicePackageChangedReceiver,
+                intentFilter);
+    }
+
+    private final class PackageChangedBroadcastReceiver extends BroadcastReceiver {
+        private final String mPackageName;
+
+        PackageChangedBroadcastReceiver(String packageName) {
+            mPackageName = packageName;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!TextUtils.equals(intent.getData().getSchemeSpecificPart(), mPackageName)) {
+                return;
+            }
+            mBackgroundExecutor.execute(
+                    GameServiceController.this::evaluateActiveGameServiceProvider);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java b/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java
deleted file mode 100644
index 7c8f251..0000000
--- a/services/core/java/com/android/server/app/GameServiceProviderConfiguration.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.app;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import java.util.Objects;
-
-/**
- * Representation of a {@link android.service.games.GameService} provider configuration.
- */
-final class GameServiceProviderConfiguration {
-    private final UserHandle mUserHandle;
-    private final ComponentName mGameServiceComponentName;
-    private final ComponentName mGameSessionServiceComponentName;
-
-    GameServiceProviderConfiguration(
-            @NonNull UserHandle userHandle,
-            @NonNull ComponentName gameServiceComponentName,
-            @NonNull ComponentName gameSessionServiceComponentName) {
-        Objects.requireNonNull(userHandle);
-        Objects.requireNonNull(gameServiceComponentName);
-        Objects.requireNonNull(gameSessionServiceComponentName);
-
-        this.mUserHandle = userHandle;
-        this.mGameServiceComponentName = gameServiceComponentName;
-        this.mGameSessionServiceComponentName = gameSessionServiceComponentName;
-    }
-
-    @NonNull
-    public UserHandle getUserHandle() {
-        return mUserHandle;
-    }
-
-    @NonNull
-    public ComponentName getGameServiceComponentName() {
-        return mGameServiceComponentName;
-    }
-
-    @NonNull
-    public ComponentName getGameSessionServiceComponentName() {
-        return mGameSessionServiceComponentName;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof GameServiceProviderConfiguration)) {
-            return false;
-        }
-
-        GameServiceProviderConfiguration that = (GameServiceProviderConfiguration) o;
-        return mUserHandle.equals(that.mUserHandle)
-                && mGameServiceComponentName.equals(that.mGameServiceComponentName)
-                && mGameSessionServiceComponentName.equals(that.mGameSessionServiceComponentName);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mUserHandle, mGameServiceComponentName,
-                mGameSessionServiceComponentName);
-    }
-
-    @Override
-    public String toString() {
-        return "GameServiceProviderConfiguration{"
-                + "mUserHandle="
-                + mUserHandle
-                + ", gameServiceComponentName="
-                + mGameServiceComponentName
-                + ", gameSessionServiceComponentName="
-                + mGameSessionServiceComponentName
-                + '}';
-    }
-}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java
index 7640cc5..7dfaec0 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactory.java
@@ -18,12 +18,13 @@
 
 import android.annotation.NonNull;
 
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
+
 /**
  * Factory for creating {@link GameServiceProviderInstance}.
  */
 interface GameServiceProviderInstanceFactory {
 
     @NonNull
-    GameServiceProviderInstance create(@NonNull
-            GameServiceProviderConfiguration gameServiceProviderConfiguration);
+    GameServiceProviderInstance create(@NonNull GameServiceComponentConfiguration configuration);
 }
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
index 73278e4..0abab6a 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
@@ -30,6 +30,7 @@
 import com.android.internal.infra.ServiceConnector;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
@@ -43,9 +44,9 @@
     @NonNull
     @Override
     public GameServiceProviderInstance create(
-            @NonNull GameServiceProviderConfiguration gameServiceProviderConfiguration) {
+            @NonNull GameServiceComponentConfiguration configuration) {
         return new GameServiceProviderInstanceImpl(
-                gameServiceProviderConfiguration.getUserHandle(),
+                configuration.getUserHandle(),
                 BackgroundThread.getExecutor(),
                 mContext,
                 new GameClassifierImpl(mContext.getPackageManager()),
@@ -53,8 +54,8 @@
                 ActivityTaskManager.getService(),
                 (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
                 LocalServices.getService(WindowManagerInternal.class),
-                new GameServiceConnector(mContext, gameServiceProviderConfiguration),
-                new GameSessionServiceConnector(mContext, gameServiceProviderConfiguration));
+                new GameServiceConnector(mContext, configuration),
+                new GameSessionServiceConnector(mContext, configuration));
     }
 
     private static final class GameServiceConnector extends ServiceConnector.Impl<IGameService> {
@@ -63,7 +64,7 @@
 
         GameServiceConnector(
                 @NonNull Context context,
-                @NonNull GameServiceProviderConfiguration configuration) {
+                @NonNull GameServiceComponentConfiguration configuration) {
             super(context, new Intent(GameService.ACTION_GAME_SERVICE)
                             .setComponent(configuration.getGameServiceComponentName()),
                     BINDING_FLAGS, configuration.getUserHandle().getIdentifier(),
@@ -86,7 +87,7 @@
 
         GameSessionServiceConnector(
                 @NonNull Context context,
-                @NonNull GameServiceProviderConfiguration configuration) {
+                @NonNull GameServiceComponentConfiguration configuration) {
             super(context, new Intent(GameSessionService.ACTION_GAME_SESSION_SERVICE)
                             .setComponent(configuration.getGameSessionServiceComponentName()),
                     BINDING_FLAGS, configuration.getUserHandle().getIdentifier(),
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 4eba771..e8d9dad 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -244,11 +244,11 @@
 
         // TODO(b/204503192): It is possible that the game service is disconnected. In this
         //  case we should avoid rebinding just to shut it down again.
-        AndroidFuture<Void> unusedPostDisconnectedFuture =
-                mGameServiceConnector.post(gameService -> {
-                    gameService.disconnected();
-                });
-        mGameServiceConnector.unbind();
+        mGameServiceConnector.post(gameService -> {
+            gameService.disconnected();
+        }).whenComplete((result, t) -> {
+            mGameServiceConnector.unbind();
+        });
         mGameSessionServiceConnector.unbind();
     }
 
diff --git a/services/core/java/com/android/server/app/GameServiceProviderSelector.java b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
index 0f55b9f..d125f21 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderSelector.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelector.java
@@ -26,10 +26,10 @@
 interface GameServiceProviderSelector {
 
     /**
-     * Returns the {@link GameServiceProviderConfiguration} associated with the selected Game
+     * Returns the {@link GameServiceConfiguration} associated with the selected Game
      * Service provider for the given user or {@code null} if none should be used.
      */
     @Nullable
-    GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user,
+    GameServiceConfiguration 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 c1ad668..fc85308 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderSelectorImpl.java
@@ -34,6 +34,7 @@
 import android.util.Xml;
 
 import com.android.server.SystemService;
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -57,7 +58,7 @@
 
     @Override
     @Nullable
-    public GameServiceProviderConfiguration get(@Nullable SystemService.TargetUser user,
+    public GameServiceConfiguration get(@Nullable SystemService.TargetUser user,
             @Nullable String packageNameOverride) {
         if (user == null) {
             return null;
@@ -98,10 +99,10 @@
 
         if (gameServiceResolveInfos == null || gameServiceResolveInfos.isEmpty()) {
             Slog.w(TAG, "No available game service found for user id: " + userId);
-            return null;
+            return new GameServiceConfiguration(gameServicePackage, null);
         }
 
-        GameServiceProviderConfiguration selectedProvider = null;
+        GameServiceConfiguration selectedProvider = null;
         for (ResolveInfo resolveInfo : gameServiceResolveInfos) {
             if (resolveInfo.serviceInfo == null) {
                 continue;
@@ -115,16 +116,18 @@
             }
 
             selectedProvider =
-                    new GameServiceProviderConfiguration(
-                            new UserHandle(userId),
-                            gameServiceServiceInfo.getComponentName(),
-                            gameSessionServiceComponentName);
+                    new GameServiceConfiguration(
+                            gameServicePackage,
+                            new GameServiceComponentConfiguration(
+                                    new UserHandle(userId),
+                                    gameServiceServiceInfo.getComponentName(),
+                                    gameSessionServiceComponentName));
             break;
         }
 
         if (selectedProvider == null) {
             Slog.w(TAG, "No valid game service found for user id: " + userId);
-            return null;
+            return new GameServiceConfiguration(gameServicePackage, null);
         }
 
         return selectedProvider;
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 90aefe0..d239c02 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -230,7 +230,9 @@
                 "Caller did not have permission while calling " + methodName);
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            // Don't log as this method can be called before user states exist as part of the
+            // force-stop check.
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ false)) {
                 return false;
             }
             final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
@@ -238,8 +240,6 @@
             if (pkgState == null
                     || !mPackageManagerInternal.canQueryPackage(
                             Binder.getCallingUid(), packageName)) {
-                Slog.e(TAG, TextUtils.formatSimple("Package %s is not installed for user %s",
-                        packageName, userId));
                 return false;
             }
             return pkgState.hibernated;
@@ -289,7 +289,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         final int realUserId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(realUserId, methodName)) {
+            if (!checkUserStatesExist(realUserId, methodName, /* shouldLog= */ true)) {
                 return;
             }
             final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId);
@@ -382,7 +382,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) {
                 return hibernatingPackages;
             }
             Map<String, UserLevelState> userStates = mUserStates.get(userId);
@@ -419,7 +419,7 @@
                 "Caller does not have MANAGE_APP_HIBERNATION permission.");
         userId = handleIncomingUser(userId, methodName);
         synchronized (mLock) {
-            if (!checkUserStatesExist(userId, methodName)) {
+            if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) {
                 return statsMap;
             }
             final Map<String, UserLevelState> userPackageStates = mUserStates.get(userId);
@@ -431,7 +431,7 @@
                 }
                 if (!mGlobalHibernationStates.containsKey(pkgName)
                         || !userPackageStates.containsKey(pkgName)) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s user %d. Maybe"
                                     + "the package was uninstalled? ", pkgName, userId));
                     continue;
@@ -585,7 +585,7 @@
                 PackageInfo pkgInfo = installedPackages.get(packageName);
                 UserLevelState currentState = diskStates.get(i);
                 if (pkgInfo == null) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s user %d. Maybe"
                                     + "the package was uninstalled? ", packageName, userId));
                     continue;
@@ -633,7 +633,7 @@
             for (int i = 0, size = diskStates.size(); i < size; i++) {
                 GlobalLevelState state = diskStates.get(i);
                 if (!installedPackages.contains(state.packageName)) {
-                    Slog.w(TAG, String.format(
+                    Slog.w(TAG, TextUtils.formatSimple(
                             "No hibernation state associated with package %s. Maybe the "
                                     + "package was uninstalled? ", state.packageName));
                     continue;
@@ -742,18 +742,24 @@
      *
      * @param userId user to check
      * @param methodName method name that is calling. Used for logging purposes.
+     * @param shouldLog whether we should log why the user state doesn't exist
      * @return true if user states exist
      */
     @GuardedBy("mLock")
-    private boolean checkUserStatesExist(int userId, String methodName) {
+    private boolean checkUserStatesExist(int userId, String methodName, boolean shouldLog) {
         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
-            Slog.e(TAG, String.format(
-                    "Attempt to call %s on stopped or nonexistent user %d", methodName, userId));
+            if (shouldLog) {
+                Slog.w(TAG, TextUtils.formatSimple(
+                        "Attempt to call %s on stopped or nonexistent user %d",
+                        methodName, userId));
+            }
             return false;
         }
         if (!mUserStates.contains(userId)) {
-            Slog.w(TAG, String.format(
-                    "Attempt to call %s before states have been read from disk", methodName));
+            if (shouldLog) {
+                Slog.w(TAG, TextUtils.formatSimple(
+                        "Attempt to call %s before states have been read from disk", methodName));
+            }
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index cebcc64f..e2d00f7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -179,6 +179,8 @@
 import com.android.server.pm.PackageList;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import libcore.util.EmptyArray;
 
 import org.json.JSONException;
@@ -5902,6 +5904,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d2fa386..567d1ae 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -30,7 +30,7 @@
 import android.app.ActivityThread;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
-import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -59,7 +59,7 @@
 import android.service.attention.AttentionService.AttentionSuccessCodes;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -336,7 +336,7 @@
      * @return {@code true} if the framework was able to dispatch the request
      */
     @VisibleForTesting
-    boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) {
+    boolean onStartProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) {
         Objects.requireNonNull(callbackInternal);
         if (!mIsServiceEnabled) {
             Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device.");
@@ -385,7 +385,7 @@
 
     /** Cancels the specified proximity registration. */
     @VisibleForTesting
-    void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) {
+    void onStopProximityUpdates(ProximityUpdateCallbackInternal callbackInternal) {
         synchronized (mLock) {
             if (mCurrentProximityUpdate == null
                     || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal)
@@ -506,12 +506,12 @@
 
         @Override
         public boolean onStartProximityUpdates(
-                ProximityCallbackInternal callback) {
+                ProximityUpdateCallbackInternal callback) {
             return AttentionManagerService.this.onStartProximityUpdates(callback);
         }
 
         @Override
-        public void onStopProximityUpdates(ProximityCallbackInternal callback) {
+        public void onStopProximityUpdates(ProximityUpdateCallbackInternal callback) {
             AttentionManagerService.this.onStopProximityUpdates(callback);
         }
     }
@@ -635,13 +635,13 @@
 
     @VisibleForTesting
     final class ProximityUpdate {
-        private final ProximityCallbackInternal mCallbackInternal;
-        private final IProximityCallback mIProximityCallback;
+        private final ProximityUpdateCallbackInternal mCallbackInternal;
+        private final IProximityUpdateCallback mIProximityUpdateCallback;
         private boolean mStartedUpdates;
 
-        ProximityUpdate(ProximityCallbackInternal callbackInternal) {
+        ProximityUpdate(ProximityUpdateCallbackInternal callbackInternal) {
             mCallbackInternal = callbackInternal;
-            mIProximityCallback = new IProximityCallback.Stub() {
+            mIProximityUpdateCallback = new IProximityUpdateCallback.Stub() {
                 @Override
                 public void onProximityUpdate(double distance) {
                     synchronized (mLock) {
@@ -664,7 +664,7 @@
                     return false;
                 }
                 try {
-                    mService.onStartProximityUpdates(mIProximityCallback);
+                    mService.onStartProximityUpdates(mIProximityUpdateCallback);
                     mStartedUpdates = true;
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
@@ -758,7 +758,8 @@
         if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
             if (mService != null) {
                 try {
-                    mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback);
+                    mService.onStartProximityUpdates(
+                            mCurrentProximityUpdate.mIProximityUpdateCallback);
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
                 }
@@ -913,7 +914,7 @@
             }
         }
 
-        class TestableProximityCallbackInternal extends ProximityCallbackInternal {
+        class TestableProximityUpdateCallbackInternal extends ProximityUpdateCallbackInternal {
             private double mLastCallbackCode = PROXIMITY_UNKNOWN;
 
             @Override
@@ -932,8 +933,8 @@
 
         final TestableAttentionCallbackInternal mTestableAttentionCallback =
                 new TestableAttentionCallbackInternal();
-        final TestableProximityCallbackInternal mTestableProximityCallback =
-                new TestableProximityCallbackInternal();
+        final TestableProximityUpdateCallbackInternal mTestableProximityUpdateCallback =
+                new TestableProximityUpdateCallbackInternal();
 
         @Override
         public int onCommand(@Nullable final String cmd) {
@@ -964,8 +965,8 @@
                         return cmdClearTestableAttentionService();
                     case "getLastTestCallbackCode":
                         return cmdGetLastTestCallbackCode();
-                    case "getLastTestProximityCallbackCode":
-                        return cmdGetLastTestProximityCallbackCode();
+                    case "getLastTestProximityUpdateCallbackCode":
+                        return cmdGetLastTestProximityUpdateCallbackCode();
                     default:
                         return handleDefaultCommands(cmd);
                 }
@@ -990,7 +991,7 @@
         private int cmdClearTestableAttentionService() {
             sTestAttentionServicePackage = "";
             mTestableAttentionCallback.reset();
-            mTestableProximityCallback.reset();
+            mTestableProximityUpdateCallback.reset();
             resetStates();
             return 0;
         }
@@ -1011,14 +1012,14 @@
 
         private int cmdCallOnStartProximityUpdates() {
             final PrintWriter out = getOutPrintWriter();
-            boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback);
+            boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityUpdateCallback);
             out.println(calledSuccessfully ? "true" : "false");
             return 0;
         }
 
         private int cmdCallOnStopProximityUpdates() {
             final PrintWriter out = getOutPrintWriter();
-            onStopProximityUpdates(mTestableProximityCallback);
+            onStopProximityUpdates(mTestableProximityUpdateCallback);
             out.println("true");
             return 0;
         }
@@ -1036,9 +1037,9 @@
             return 0;
         }
 
-        private int cmdGetLastTestProximityCallbackCode() {
+        private int cmdGetLastTestProximityUpdateCallbackCode() {
             final PrintWriter out = getOutPrintWriter();
-            out.println(mTestableProximityCallback.getLastCallbackCode());
+            out.println(mTestableProximityUpdateCallback.getLastCallbackCode());
             return 0;
         }
 
@@ -1081,7 +1082,7 @@
             out.println(
                     "       := true, if the request was successfully dispatched to the service "
                             + "implementation."
-                            + " (to see the result, call getLastTestProximityCallbackCode)");
+                            + " (to see the result, call getLastTestProximityUpdateCallbackCode)");
             out.println("       := false, otherwise");
             out.println("  call onStopProximityUpdates: Cancels proximity updates");
             out.println("  getLastTestCallbackCode");
@@ -1089,7 +1090,7 @@
             out.println(
                     "       := An integer, representing the last callback code received from the "
                             + "bounded implementation. If none, it will return -1");
-            out.println("  getLastTestProximityCallbackCode");
+            out.println("  getLastTestProximityUpdateCallbackCode");
             out.println("  ---returns:");
             out.println(
                     "       := A double, representing the last proximity value received from the "
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3e5e435..a67e6af 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -36,6 +36,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -87,6 +88,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.BluetoothProfileConnectionInfo;
+import android.media.IAudioDeviceVolumeDispatcher;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioModeDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -108,6 +110,7 @@
 import android.media.MediaRecorder.AudioSource;
 import android.media.PlayerBase;
 import android.media.Spatializer;
+import android.media.VolumeInfo;
 import android.media.VolumePolicy;
 import android.media.audiofx.AudioEffect;
 import android.media.audiopolicy.AudioMix;
@@ -124,6 +127,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HwBinder;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -3327,6 +3331,13 @@
         }
     }
 
+    private void enforceAccessUltrasoundPermission() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Missing ACCESS_ULTRASOUND permission");
+        }
+    }
+
     private void enforceQueryStatePermission() {
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -3459,6 +3470,12 @@
                 attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
     }
 
+    /** @see AudioManager#isUltrasoundSupported() */
+    public boolean isUltrasoundSupported() {
+        enforceAccessUltrasoundPermission();
+        return AudioSystem.isUltrasoundSupported();
+    }
+
     private boolean canChangeAccessibilityVolume() {
         synchronized (mAccessibilityServiceUidsLock) {
             if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
@@ -4246,6 +4263,24 @@
         return (mStreamStates[streamType].getIndex(device) + 5) / 10;
     }
 
+    /**
+     * Default VolumeInfo returned by {@link VolumeInfo#getDefaultVolumeInfo()}
+     * Lazily initialized in {@link #getDefaultVolumeInfo()}
+     */
+    static VolumeInfo sDefaultVolumeInfo;
+
+    /** @see VolumeInfo#getDefaultVolumeInfo() */
+    public VolumeInfo getDefaultVolumeInfo() {
+        if (sDefaultVolumeInfo == null) {
+            sDefaultVolumeInfo = new VolumeInfo.Builder(AudioSystem.STREAM_MUSIC)
+                    .setMinVolumeIndex(getStreamMinVolume(AudioSystem.STREAM_MUSIC))
+                    .setMaxVolumeIndex(getStreamMaxVolume(AudioSystem.STREAM_MUSIC))
+                    .setMuted(false)
+                    .build();
+        }
+        return sDefaultVolumeInfo;
+    }
+
     /** @see AudioManager#getUiSoundsStreamType()
      * TODO(b/181140246): when using VolumeGroup alias, we are lacking configurability for
      * UI Sounds identification.
@@ -6310,6 +6345,37 @@
     }
 
     /**
+     * @see AudioDeviceVolumeManager#setDeviceAbsoluteMultiVolumeBehavior
+     * @param cb
+     * @param attr
+     * @param volumes
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    public void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
+            IAudioDeviceVolumeDispatcher cb, String packageName,
+            AudioDeviceAttributes device, List<VolumeInfo> volumes) {
+        // verify permissions
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                != PackageManager.PERMISSION_GRANTED
+                && mContext.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Missing MODIFY_AUDIO_ROUTING or BLUETOOTH_PRIVILEGED permissions");
+        }
+        // verify arguments
+        Objects.requireNonNull(device);
+        Objects.requireNonNull(volumes);
+
+        // current implementation maps this call to existing abs volume API of AudioManager
+        // TODO implement the volume/device listener through IAudioDeviceVolumeDispatcher
+        final int volumeBehavior = volumes.size() == 1
+                ? AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
+                : AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
+        setDeviceVolumeBehavior(device, volumeBehavior, packageName);
+    }
+
+    /**
      * @see AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)
      * @param device the audio device to be affected
      * @param deviceVolumeBehavior one of the device behaviors
@@ -10345,6 +10411,25 @@
         return mMediaFocusControl.sendFocusLoss(focusLoser);
     }
 
+    private static final String[] HAL_VERSIONS = new String[] {"7.1", "7.0", "6.0", "4.0", "2.0"};
+
+    /** @see AudioManager#getHalVersion */
+    public @Nullable String getHalVersion() {
+        for (String version : HAL_VERSIONS) {
+            try {
+                HwBinder.getService(
+                        String.format("android.hardware.audio@%s::IDevicesFactory", version),
+                        "default");
+                return version;
+            } catch (NoSuchElementException e) {
+                // Ignore, the specified HAL interface is not found.
+            } catch (RemoteException re) {
+                Log.e(TAG, "Remote exception when getting hardware audio service:", re);
+            }
+        }
+        return null;
+    }
+
     /** see AudioManager.hasRegisteredDynamicPolicy */
     public boolean hasRegisteredDynamicPolicy() {
         synchronized (mAudioPolicies) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index eb2f80b..9f46bd6 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -389,7 +389,7 @@
             final long oldIdentity = Binder.clearCallingIdentity();
             try {
                 if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
-                        PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+                        PROPERTY_AUTO_CLEAR_ENABLED, true)) {
                     mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
                             userId);
                     Message clearMessage = Message.obtain(mClipboardClearHandler,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ea054a5..682f0df 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2020,7 +2020,8 @@
                             .setCategory(Notification.CATEGORY_SYSTEM)
                             .setVisibility(Notification.VISIBILITY_PUBLIC)
                             .setOngoing(true)
-                            .setColor(mContext.getColor(R.color.system_notification_accent_color));
+                            .setColor(mContext.getColor(
+                                    android.R.color.system_notification_accent_color));
             notificationManager.notify(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, builder.build());
         } finally {
             Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index f4aa88f..73afa60 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -116,6 +116,8 @@
 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
 import com.android.server.job.JobSchedulerInternal;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -2169,6 +2171,7 @@
         return true;
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) {
         final StringBuilder sb = new StringBuilder();
 
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2c2a2bf..17215e5 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -131,6 +131,7 @@
     private static final int MSG_STOP_SENSOR_LISTENER = 2;
     private static final int MSG_START_SENSOR_LISTENER = 3;
     private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
+    private static final int MSG_SENSOR_CHANGED = 5;
 
     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
@@ -158,6 +159,7 @@
     // These members should only be accessed on the mBgHandler thread.
     private BroadcastReceiver mBroadcastReceiver;
     private SensorListener mSensorListener;
+    private Sensor mLightSensor;
     private SettingsObserver mSettingsObserver;
     private DisplayListener mDisplayListener;
     private boolean mSensorRegistered;
@@ -327,6 +329,14 @@
         m.sendToTarget();
     }
 
+    /**
+     * Updates the light sensor to use.
+     */
+    public void setLightSensor(Sensor lightSensor) {
+        mBgHandler.obtainMessage(MSG_SENSOR_CHANGED, 0 /*unused*/, 0/*unused*/, lightSensor)
+                .sendToTarget();
+    }
+
     private void handleBrightnessChanged(float brightness, boolean userInitiated,
             float powerBrightnessFactor, boolean isUserSetBrightness,
             boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
@@ -428,13 +438,28 @@
         }
     }
 
+    private void handleSensorChanged(Sensor lightSensor) {
+        if (mLightSensor != lightSensor) {
+            mLightSensor = lightSensor;
+            stopSensorListener();
+            synchronized (mDataCollectionLock) {
+                mLastSensorReadings.clear();
+            }
+            // Attempt to restart the sensor listener. It will check to see if it should be running
+            // so there is no need to also check here.
+            startSensorListener();
+        }
+    }
+
     private void startSensorListener() {
         if (!mSensorRegistered
+                && mLightSensor != null
+                && mAmbientBrightnessStatsTracker != null
                 && mInjector.isInteractive(mContext)
                 && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
             mAmbientBrightnessStatsTracker.start();
             mSensorRegistered = true;
-            mInjector.registerSensorListener(mContext, mSensorListener,
+            mInjector.registerSensorListener(mContext, mSensorListener, mLightSensor,
                     mInjector.getBackgroundHandler());
         }
     }
@@ -736,6 +761,7 @@
         pw.println("BrightnessTracker state:");
         synchronized (mDataCollectionLock) {
             pw.println("  mStarted=" + mStarted);
+            pw.println("  mLightSensor=" + mLightSensor);
             pw.println("  mLastBatteryLevel=" + mLastBatteryLevel);
             pw.println("  mLastBrightness=" + mLastBrightness);
             pw.println("  mLastSensorReadings.size=" + mLastSensorReadings.size());
@@ -1017,6 +1043,9 @@
                         disableColorSampling();
                     }
                     break;
+                case MSG_SENSOR_CHANGED:
+                    handleSensorChanged((Sensor) msg.obj);
+                    break;
 
             }
         }
@@ -1045,9 +1074,8 @@
     @VisibleForTesting
     static class Injector {
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener, Handler handler) {
+                SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
             SensorManager sensorManager = context.getSystemService(SensorManager.class);
-            Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
             sensorManager.registerListener(sensorListener,
                     lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
         }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 6866137..064e307 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -42,7 +42,6 @@
 import com.android.server.display.config.DisplayQuirks;
 import com.android.server.display.config.HbmTiming;
 import com.android.server.display.config.HighBrightnessMode;
-import com.android.server.display.config.Interpolation;
 import com.android.server.display.config.NitsMap;
 import com.android.server.display.config.Point;
 import com.android.server.display.config.RefreshRateRange;
@@ -1254,19 +1253,17 @@
         }
     }
 
-    private int convertInterpolationType(Interpolation value) {
-        if (value == null) {
+    private int convertInterpolationType(String value) {
+        if (TextUtils.isEmpty(value)) {
             return INTERPOLATION_DEFAULT;
         }
-        switch (value) {
-            case _default:
-                return INTERPOLATION_DEFAULT;
-            case linear:
-                return INTERPOLATION_LINEAR;
-            default:
-                Slog.wtf(TAG, "Unexpected Interpolation Type: " + value);
-                return INTERPOLATION_DEFAULT;
+
+        if ("linear".equals(value)) {
+            return INTERPOLATION_LINEAR;
         }
+
+        Slog.wtf(TAG, "Unexpected Interpolation Type: " + value);
+        return INTERPOLATION_DEFAULT;
     }
 
     private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index f5001102..7550d7e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3952,12 +3952,22 @@
      * Listens to changes in device state and reports the state to LogicalDisplayMapper.
      */
     class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
+        // Base state corresponds to the physical state of the device
+        private int mBaseState = DeviceStateManager.INVALID_DEVICE_STATE;
+
         @Override
         public void onStateChanged(int deviceState) {
+            boolean isDeviceStateOverrideActive = deviceState != mBaseState;
             synchronized (mSyncRoot) {
-                mLogicalDisplayMapper.setDeviceStateLocked(deviceState);
+                mLogicalDisplayMapper
+                        .setDeviceStateLocked(deviceState, isDeviceStateOverrideActive);
             }
         }
+
+        @Override
+        public void onBaseStateChanged(int state) {
+            mBaseState = state;
+        }
     };
 
     private class BrightnessPair {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 418e91d..accdd56 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -63,6 +63,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.RingBuffer;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -155,6 +156,8 @@
     private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;
     private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
 
+    private static final int RINGBUFFER_MAX = 100;
+
     private final String TAG;
 
     private final Object mLock = new Object();
@@ -212,6 +215,9 @@
 
     private final float mScreenBrightnessDefault;
 
+    // Previously logged screen brightness. Used for autobrightness event dumpsys.
+    private float mPreviousScreenBrightness = Float.NaN;
+
     // The minimum allowed brightness while in VR.
     private final float mScreenBrightnessForVrRangeMinimum;
 
@@ -387,6 +393,9 @@
 
     private final Runnable mOnBrightnessChangeRunnable;
 
+    // Used for keeping record in dumpsys for when and to which brightness auto adaptions were made.
+    private RingBuffer<AutobrightnessEvent> mAutobrightnessEventRingBuffer;
+
     // A record of state for skipping brightness ramps.
     private int mSkipRampState = RAMP_STATE_SKIP_NONE;
 
@@ -826,7 +835,6 @@
     private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
         // All properties that depend on the associated DisplayDevice and the DDC must be
         // updated here.
-        loadAmbientLightSensor();
         loadBrightnessRampRates();
         loadProximitySensor();
         loadNitsRange(mContext.getResources());
@@ -972,6 +980,9 @@
             }
 
             loadAmbientLightSensor();
+            if (mBrightnessTracker != null) {
+                mBrightnessTracker.setLightSensor(mLightSensor);
+            }
 
             if (mAutomaticBrightnessController != null) {
                 mAutomaticBrightnessController.stop();
@@ -986,6 +997,9 @@
                     mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
                     mDisplayDeviceConfig.getAmbientHorizonShort(),
                     mDisplayDeviceConfig.getAmbientHorizonLong());
+
+            mAutobrightnessEventRingBuffer =
+                    new RingBuffer<>(AutobrightnessEvent.class, RINGBUFFER_MAX);
         } else {
             mUseSoftwareAutoBrightnessConfig = false;
         }
@@ -1556,6 +1570,15 @@
             Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
         }
 
+        // Add any automatic changes to autobrightness ringbuffer for dumpsys.
+        if (mBrightnessReason.reason == BrightnessReason.REASON_AUTOMATIC
+                && !BrightnessSynchronizer.floatEquals(
+                        mPreviousScreenBrightness, brightnessState)) {
+            mPreviousScreenBrightness = brightnessState;
+            mAutobrightnessEventRingBuffer.append(new AutobrightnessEvent(
+                    System.currentTimeMillis(), brightnessState));
+        }
+
         // Update display white-balance.
         if (mDisplayWhiteBalanceController != null) {
             if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
@@ -2489,6 +2512,7 @@
 
         if (mAutomaticBrightnessController != null) {
             mAutomaticBrightnessController.dump(pw);
+            dumpAutobrightnessEvents(pw);
         }
 
         if (mHbmController != null) {
@@ -2545,6 +2569,20 @@
         }
     }
 
+    private void dumpAutobrightnessEvents(PrintWriter pw) {
+        int size = mAutobrightnessEventRingBuffer.size();
+        if (size < 1) {
+            pw.println("No Automatic Brightness Adjustments");
+            return;
+        }
+
+        pw.println("Automatic Brightness Adjustments Last " + size + " Events: ");
+        AutobrightnessEvent[] eventArray = mAutobrightnessEventRingBuffer.toArray();
+        for (int i = 0; i < mAutobrightnessEventRingBuffer.size(); i++) {
+            pw.println("  " + eventArray[i].toString());
+        }
+    }
+
     private static float clampAbsoluteBrightness(float value) {
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
                 PowerManager.BRIGHTNESS_MAX);
@@ -2613,6 +2651,21 @@
         }
     }
 
+    private static class AutobrightnessEvent {
+        final long mTime;
+        final float mBrightness;
+
+        AutobrightnessEvent(long time, float brightness) {
+            mTime = time;
+            mBrightness = brightness;
+        }
+
+        @Override
+        public String toString() {
+            return TimeUtils.formatForLogging(mTime) + " - Brightness: " + mBrightness;
+        }
+    }
+
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
             super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d233c5e..2e80efb 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -255,10 +255,9 @@
         public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
                 SurfaceControl.DynamicDisplayInfo dynamicInfo,
                 SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
-            boolean changed =
-                    updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
-            changed |= updateDisplayModesLocked(
-                    dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
+            boolean changed = updateDisplayModesLocked(
+                    dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode,
+                    dynamicInfo.activeDisplayModeId, modeSpecs);
             changed |= updateStaticInfo(staticInfo);
             changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
                     dynamicInfo.activeColorMode);
@@ -273,10 +272,12 @@
         }
 
         public boolean updateDisplayModesLocked(
-                SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
-                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+                SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId,
+                int activeSfDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
             mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
-            mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId);
+            mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId);
+            SurfaceControl.DisplayMode preferredSfDisplayMode =
+                        getModeById(displayModes, preferredSfDisplayModeId);
 
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<>();
@@ -335,6 +336,27 @@
                 }
             }
 
+            boolean preferredModeChanged = false;
+
+            if (preferredSfDisplayModeId != INVALID_MODE_ID && preferredSfDisplayMode != null) {
+                DisplayModeRecord preferredRecord = null;
+                for (DisplayModeRecord record : records) {
+                    if (record.hasMatchingMode(preferredSfDisplayMode)) {
+                        preferredRecord = record;
+                        break;
+                    }
+                }
+
+                if (preferredRecord != null) {
+                    int preferredModeId = preferredRecord.mMode.getModeId();
+                    if (mSurfaceControlProxy.getBootDisplayModeSupport()
+                            && mSystemPreferredModeId != preferredModeId) {
+                        mSystemPreferredModeId = preferredModeId;
+                        preferredModeChanged = true;
+                    }
+                }
+            }
+
             boolean activeModeChanged = false;
 
             // Check whether SurfaceFlinger or the display device changed the active mode out from
@@ -373,7 +395,7 @@
             boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
             // If the records haven't changed then we're done here.
             if (!recordsChanged) {
-                return activeModeChanged;
+                return activeModeChanged || preferredModeChanged;
             }
 
             mSupportedModes.clear();
@@ -386,14 +408,14 @@
                 mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
                         ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
                 mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
-                        ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+                        ? preferredSfDisplayMode.group
                         : mActiveSfDisplayMode.group;
             } else if (modesAdded && activeModeChanged) {
                 Slog.d(TAG, "New display modes are added and the active mode has changed, "
                         + "use active mode as default mode.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
                 mDefaultModeGroup = mActiveSfDisplayMode.group;
-            } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
+            } else if (findSfDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
                 Slog.w(TAG, "Default display mode no longer available, using currently"
                         + " active mode as default.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
@@ -545,15 +567,6 @@
             return true;
         }
 
-        private boolean updateSystemPreferredDisplayMode(int modeId) {
-            if (!mSurfaceControlProxy.getBootDisplayModeSupport()
-                    || mSystemPreferredModeId == modeId) {
-                return false;
-            }
-            mSystemPreferredModeId = modeId;
-            return true;
-        }
-
         private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
                 int modeId) {
             for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -887,8 +900,10 @@
             if (mUserPreferredMode == null) {
                 mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
             } else {
+                int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
+                        mUserPreferredMode.getModeId(), mDefaultModeGroup);
                 mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
-                        mUserPreferredMode.getModeId());
+                        preferredSfDisplayModeId);
             }
         }
 
@@ -923,9 +938,9 @@
             // mode based on vendor requirements.
             // Note: We prefer the default mode group over the current one as this is the mode
             // group the vendor prefers.
-            int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+            int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId,
                     mDefaultModeGroup);
-            if (baseModeId < 0) {
+            if (baseSfModeId < 0) {
                 // When a display is hotplugged, it's possible for a mode to be removed that was
                 // previously valid. Because of the way display changes are propagated through the
                 // framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -943,7 +958,7 @@
                 getHandler().sendMessage(PooledLambda.obtainMessage(
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
-                        new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
+                        new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
                                 mDisplayModeSpecs.allowGroupSwitching,
                                 mDisplayModeSpecs.primaryRefreshRateRange.min,
                                 mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -1090,14 +1105,14 @@
             pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
         }
 
-        private int findDisplayModeIdLocked(int modeId, int modeGroup) {
-            int matchingModeId = INVALID_MODE_ID;
-            DisplayModeRecord record = mSupportedModes.get(modeId);
+        private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
+            int matchingSfDisplayModeId = INVALID_MODE_ID;
+            DisplayModeRecord record = mSupportedModes.get(displayModeId);
             if (record != null) {
                 for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
                     if (record.hasMatchingMode(mode)) {
-                        if (matchingModeId == INVALID_MODE_ID) {
-                            matchingModeId = mode.id;
+                        if (matchingSfDisplayModeId == INVALID_MODE_ID) {
+                            matchingSfDisplayModeId = mode.id;
                         }
 
                         // Prefer to return a mode that matches the modeGroup
@@ -1107,7 +1122,7 @@
                     }
                 }
             }
-            return matchingModeId;
+            return matchingSfDisplayModeId;
         }
 
         // Returns a mode with id = modeId.
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 6f5729f..add0a9f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -359,7 +359,7 @@
         mDeviceStateToLayoutMap.dumpLocked(ipw);
     }
 
-    void setDeviceStateLocked(int state) {
+    void setDeviceStateLocked(int state, boolean isOverrideActive) {
         Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
                 + ", interactive=" + mInteractive);
         // As part of a state transition, we may need to turn off some displays temporarily so that
@@ -370,12 +370,10 @@
             resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
         }
         mPendingDeviceState = state;
-        final boolean wakeDevice = mDeviceStatesOnWhichToWakeUp.get(mPendingDeviceState)
-                && !mDeviceStatesOnWhichToWakeUp.get(mDeviceState)
-                && !mInteractive && mBootCompleted;
-        final boolean sleepDevice = mDeviceStatesOnWhichToSleep.get(mPendingDeviceState)
-                && !mDeviceStatesOnWhichToSleep.get(mDeviceState)
-                && mInteractive && mBootCompleted;
+        final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
+                mInteractive, mBootCompleted);
+        final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
+                isOverrideActive, mInteractive, mBootCompleted);
 
         // If all displays are off already, we can just transition here, unless we are trying to
         // wake or sleep the device as part of this transition. In that case defer the final
@@ -429,6 +427,53 @@
         }
     }
 
+    /**
+     * Returns if the device should be woken up or not. Called to check if the device state we are
+     * moving to is one that should awake the device, as well as if we are moving from a device
+     * state that shouldn't have been already woken from.
+     *
+     * @param pendingState device state we are moving to
+     * @param currentState device state we are currently in
+     * @param isInteractive if the device is in an interactive state
+     * @param isBootCompleted is the device fully booted
+     *
+     * @see #shouldDeviceBePutToSleep
+     * @see #setDeviceStateLocked
+     */
+    @VisibleForTesting
+    boolean shouldDeviceBeWoken(int pendingState, int currentState, boolean isInteractive,
+            boolean isBootCompleted) {
+        return mDeviceStatesOnWhichToWakeUp.get(pendingState)
+                && !mDeviceStatesOnWhichToWakeUp.get(currentState)
+                && !isInteractive && isBootCompleted;
+    }
+
+    /**
+     * Returns if the device should be put to sleep or not.
+     *
+     * Includes a check to verify that the device state that we are moving to, {@code pendingState},
+     * is the same as the physical state of the device, {@code baseState}. Different values for
+     * these parameters indicate a device state override is active, and we shouldn't put the device
+     * to sleep to provide a better user experience.
+     *
+     * @param pendingState device state we are moving to
+     * @param currentState device state we are currently in
+     * @param isOverrideActive if a device state override is currently active or not
+     * @param isInteractive if the device is in an interactive state
+     * @param isBootCompleted is the device fully booted
+     *
+     * @see #shouldDeviceBeWoken
+     * @see #setDeviceStateLocked
+     */
+    @VisibleForTesting
+    boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive,
+            boolean isInteractive, boolean isBootCompleted) {
+        return mDeviceStatesOnWhichToSleep.get(pendingState)
+                && !mDeviceStatesOnWhichToSleep.get(currentState)
+                && !isOverrideActive
+                && isInteractive && isBootCompleted;
+    }
+
     private boolean areAllTransitioningDisplaysOffLocked() {
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index cb93cc8..f529c4c 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -98,6 +98,13 @@
         return ColorDisplayManager.isColorTransformAccelerated(context);
     }
 
+    @Override
+    public void setActivated(Boolean isActivated) {
+        super.setActivated(isActivated);
+        Slog.i(ColorDisplayService.TAG, (isActivated != null && isActivated)
+                ? "Turning on reduce bright colors" : "Turning off reduce bright colors");
+    }
+
     public int getStrength() {
         return mStrength;
     }
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 1139d28..bb8a744 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -24,6 +24,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.Handler;
@@ -38,6 +39,8 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.pm.Computer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -75,6 +78,9 @@
 
     private final RuleObserver mObserver;
 
+    @NonNull
+    private PackageManagerInternal mPackageManager;
+
     private FirewallIntentResolver mActivityResolver = new FirewallIntentResolver();
     private FirewallIntentResolver mBroadcastResolver = new FirewallIntentResolver();
     private FirewallIntentResolver mServiceResolver = new FirewallIntentResolver();
@@ -123,6 +129,13 @@
         mObserver.startWatching();
     }
 
+    private PackageManagerInternal getPackageManager() {
+        if (mPackageManager == null) {
+            mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPackageManager;
+    }
+
     /**
      * This is called from ActivityManager to check if a start activity intent should be allowed.
      * It is assumed the caller is already holding the global ActivityManagerService lock.
@@ -154,7 +167,8 @@
         // For the first pass, find all the rules that have at least one intent-filter or
         // component-filter that matches this intent
         List<Rule> candidateRules;
-        candidateRules = resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, 0);
+        candidateRules = resolver.queryIntent(getPackageManager().snapshot(), intent, resolvedType,
+                false /*defaultOnly*/, 0);
         if (candidateRules == null) {
             candidateRules = new ArrayList<Rule>();
         }
@@ -375,7 +389,7 @@
             for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
                 Rule rule = rules.get(ruleIndex);
                 for (int i=0; i<rule.getIntentFilterCount(); i++) {
-                    resolver.addFilter(rule.getIntentFilter(i));
+                    resolver.addFilter(null, rule.getIntentFilter(i));
                 }
                 for (int i=0; i<rule.getComponentFilterCount(); i++) {
                     resolver.addComponentFilter(rule.getComponentFilter(i), rule);
@@ -512,7 +526,8 @@
         }
 
         @Override
-        protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+        protected Rule newResult(@NonNull Computer computer, FirewallIntentFilter filter,
+                int match, int userId, long customFlags) {
             return filter.rule;
         }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 940c25c..c15242a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -41,6 +41,9 @@
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
 import android.graphics.PointF;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors;
+import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayViewport;
 import android.hardware.input.IInputDevicesChangedListener;
@@ -550,6 +553,19 @@
             }
         }
 
+        // Set the HW mic toggle switch state
+        final int micMuteState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY,
+                SW_MUTE_DEVICE);
+        if (micMuteState != InputManager.SWITCH_STATE_UNKNOWN) {
+            setSensorPrivacy(Sensors.MICROPHONE, micMuteState != InputManager.SWITCH_STATE_OFF);
+        }
+        // Set the HW camera toggle switch state
+        final int cameraMuteState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY,
+                SW_CAMERA_LENS_COVER);
+        if (cameraMuteState != InputManager.SWITCH_STATE_UNKNOWN) {
+            setSensorPrivacy(Sensors.CAMERA, cameraMuteState != InputManager.SWITCH_STATE_OFF);
+        }
+
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
@@ -2816,6 +2832,8 @@
         if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
             final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
             mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
+            // Use SW_CAMERA_LENS_COVER code for camera privacy toggles
+            setSensorPrivacy(Sensors.CAMERA, lensCovered);
         }
 
         if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
@@ -2836,9 +2854,20 @@
             final boolean micMute = ((switchValues & SW_MUTE_DEVICE_BIT) != 0);
             AudioManager audioManager = mContext.getSystemService(AudioManager.class);
             audioManager.setMicrophoneMuteFromSwitch(micMute);
+
+            setSensorPrivacy(Sensors.MICROPHONE, micMute);
         }
     }
 
+    // Set the sensor privacy state based on the hardware toggles switch states
+    private void setSensorPrivacy(@SensorPrivacyManager.Sensors.Sensor int sensor,
+            boolean enablePrivacy) {
+        final SensorPrivacyManagerInternal sensorPrivacyManagerInternal =
+                LocalServices.getService(SensorPrivacyManagerInternal.class);
+        sensorPrivacyManagerInternal.setPhysicalToggleSensorPrivacy(UserHandle.USER_CURRENT, sensor,
+                enablePrivacy);
+    }
+
     // Native callback.
     @SuppressWarnings("unused")
     private void notifyInputChannelBroken(IBinder token) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b2f500a..d2d80ff 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -408,6 +408,11 @@
     @GuardedBy("ImfLock.class")
     @NonNull
     InputBindResult bindCurrentMethod() {
+        if (mSelectedMethodId == null) {
+            Slog.e(TAG, "mSelectedMethodId is null!");
+            return InputBindResult.NO_IME;
+        }
+
         InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
         if (info == null) {
             throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7068ed1..0b7e391 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -130,6 +130,7 @@
 import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InlineSuggestionsRequest;
@@ -287,6 +288,9 @@
     private final InputMethodMenuController mMenuController;
     private final InputMethodBindingController mBindingController;
 
+    // TODO(b/219056452): Use AccessibilityManagerInternal instead.
+    private final AccessibilityManager mAccessibilityManager;
+
     /**
      * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}.
      *
@@ -1627,6 +1631,7 @@
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
         mPlatformCompat = IPlatformCompat.Stub.asInterface(
@@ -1995,12 +2000,13 @@
 
     @GuardedBy("ImfLock.class")
     private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
-            InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
+            InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
+            boolean touchExplorationEnabled) {
         final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
         try {
             IInputMethodInvoker curMethod = getCurMethodLocked();
-            if (userId == mSettings.getCurrentUserId() && imi != null
-                    && imi.isInlineSuggestionsEnabled() && curMethod != null) {
+            if (userId == mSettings.getCurrentUserId() && curMethod != null
+                    && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
                 final IInlineSuggestionsRequestCallback callbackImpl =
                         new InlineSuggestionsRequestCallbackDecorator(callback,
                                 imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(),
@@ -2014,6 +2020,13 @@
         }
     }
 
+    private static boolean isInlineSuggestionsEnabled(InputMethodInfo imi,
+            boolean touchExplorationEnabled) {
+        return imi.isInlineSuggestionsEnabled()
+                && (!touchExplorationEnabled
+                    || imi.supportsInlineSuggestionsWithTouchExploration());
+    }
+
     /**
      * The decorator which validates the host package name in the
      * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
@@ -5197,8 +5210,12 @@
         @Override
         public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
                 InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
+            // Get the device global touch exploration state before lock to avoid deadlock.
+            boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
+
             synchronized (ImfLock.class) {
-                onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb);
+                onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb,
+                        touchExplorationEnabled);
             }
         }
 
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 0c3f9f0..45d9822 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1428,6 +1428,7 @@
         ipw.println("Location Settings:");
         ipw.increaseIndent();
         mInjector.getSettingsHelper().dump(fd, ipw, args);
+        mInjector.getLocationSettings().dump(fd, ipw, args);
         ipw.decreaseIndent();
 
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 8fdde24..e9bf90f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -16,8 +16,6 @@
 
 package com.android.server.location.contexthub;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.Manifest;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.AsyncEventType;
@@ -297,19 +295,14 @@
     }
 
     /**
-     * Checks for location hardware permissions.
+     * Checks for ACCESS_CONTEXT_HUB permissions.
      *
      * @param context the context of the service
      */
     /* package */
     static void checkPermissions(Context context) {
-        boolean hasAccessContextHubPermission = (context.checkCallingPermission(
-                CONTEXT_HUB_PERMISSION) == PERMISSION_GRANTED);
-
-        if (!hasAccessContextHubPermission) {
-            throw new SecurityException(
-                    "ACCESS_CONTEXT_HUB permission required to use Context Hub");
-        }
+        context.enforceCallingOrSelfPermission(CONTEXT_HUB_PERMISSION,
+                "ACCESS_CONTEXT_HUB permission required to use Context Hub");
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index d42e2c6..0b8f94c 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1607,6 +1607,8 @@
 
     public @Nullable Location getLastLocation(LastLocationRequest request,
             CallerIdentity identity, @PermissionLevel int permissionLevel) {
+        request = calculateLastLocationRequest(request);
+
         if (!isActive(request.isBypass(), identity)) {
             return null;
         }
@@ -1634,6 +1636,38 @@
         return location;
     }
 
+    private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest) {
+        LastLocationRequest.Builder builder = new LastLocationRequest.Builder(baseRequest);
+
+        boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored();
+        if (locationSettingsIgnored) {
+            // if we are not currently allowed use location settings ignored, disable it
+            if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
+                    getIdentity().getPackageName(), getIdentity().getAttributionTag())
+                    && !mLocationManagerInternal.isProvider(null, getIdentity())) {
+                locationSettingsIgnored = false;
+            }
+
+            builder.setLocationSettingsIgnored(locationSettingsIgnored);
+        }
+
+        boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+        if (adasGnssBypass) {
+            // if we are not currently allowed use adas gnss bypass, disable it
+            if (!GPS_PROVIDER.equals(mName)) {
+                Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+                adasGnssBypass = false;
+            } else if (!mLocationSettings.getUserSettings(
+                    getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+                adasGnssBypass = false;
+            }
+
+            builder.setAdasGnssBypass(adasGnssBypass);
+        }
+
+        return builder.build();
+    }
+
     /**
      * This function does not perform any permissions or safety checks, by calling it you are
      * committing to performing all applicable checks yourself. This always returns a "fine"
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
index d521538..be0e7ac 100644
--- a/services/core/java/com/android/server/location/settings/LocationSettings.java
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -18,8 +18,11 @@
 
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Environment;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,6 +32,7 @@
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Function;
@@ -104,6 +108,33 @@
         getUserSettingsStore(userId).update(updater);
     }
 
+    /** Dumps info for debugging. */
+    public final void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+        int[] userIds;
+        try {
+            userIds = ActivityManager.getService().getRunningUserIds();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        if (mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+            ipw.print("ADAS Location Setting: ");
+            ipw.increaseIndent();
+            if (userIds.length > 1) {
+                ipw.println();
+                for (int userId : userIds) {
+                    ipw.print("[u");
+                    ipw.print(userId);
+                    ipw.print("] ");
+                    ipw.println(getUserSettings(userId).isAdasGnssLocationEnabled());
+                }
+            } else {
+                ipw.println(getUserSettings(userIds[0]).isAdasGnssLocationEnabled());
+            }
+            ipw.decreaseIndent();
+        }
+    }
+
     @VisibleForTesting
     final void flushFiles() throws InterruptedException {
         synchronized (mUserSettings) {
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 34614d5..490e00e 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -329,6 +329,20 @@
 
             if (mStart) {
 
+                ActivityManagerInternal ami = LocalServices.getService(
+                        ActivityManagerInternal.class);
+                boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(mUid);
+
+                // The instrumented apks only run for testing, so we don't check user permission.
+                if (isCallerInstrumented) {
+                    try {
+                        getLogdService().approve(mUid, mGid, mPid, mFd);
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
+                    return;
+                }
+
                 // TODO Temporarily approve all the requests to unblock testing failures.
                 try {
                     getLogdService().approve(mUid, mGid, mPid, mFd);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 851ea3d..1b7d1ba 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -293,7 +293,7 @@
                         .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset),
                                 mResetIntent)
                         .setColor(mContext.getColor(
-                                com.android.internal.R.color.system_notification_accent_color));
+                                android.R.color.system_notification_accent_color));
 
         mNotificationManager.notify(null /* tag */, SystemMessage.NOTE_VPN_STATUS,
                 builder.build());
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8d05415..60962b1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -257,6 +257,8 @@
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import libcore.io.IoUtils;
 
 import java.io.File;
@@ -3748,6 +3750,7 @@
         return 0;
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
@@ -4154,7 +4157,7 @@
         if (mRestrictedNetworkingMode) {
             // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
             // In this case, default firewall rules can also be added.
-            setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid,
+            setUidFirewallRuleUL(FIREWALL_CHAIN_RESTRICTED, uid,
                     getRestrictedModeFirewallRule(uidBlockedState));
         }
     }
@@ -4318,10 +4321,10 @@
                 && (uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY)
                 == 0) {
             mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
-            setUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
+            setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
         } else {
             mUidFirewallLowPowerStandbyModeRules.delete(uid);
-            setUidFirewallRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+            setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
         }
     }
 
@@ -4370,9 +4373,9 @@
             final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
                     chain == FIREWALL_CHAIN_DOZABLE);
             if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
-                setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
+                setUidFirewallRuleUL(chain, uid, FIREWALL_RULE_ALLOW);
             } else {
-                setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT);
+                setUidFirewallRuleUL(chain, uid, FIREWALL_RULE_DEFAULT);
             }
         }
     }
@@ -4418,10 +4421,10 @@
             int appId = UserHandle.getAppId(uid);
             if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
                     && !isUidForegroundOnRestrictPowerUL(uid)) {
-                setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+                setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
                 if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
             } else {
-                setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+                setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
                 if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
             }
         } finally {
@@ -5492,10 +5495,11 @@
     /**
      * Add or remove a uid to the firewall denylist for all network ifaces.
      */
-    private void setUidFirewallRule(int chain, int uid, int rule) {
+    @GuardedBy("mUidRulesFirstLock")
+    private void setUidFirewallRuleUL(int chain, int uid, int rule) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
-                    "setUidFirewallRule: " + chain + "/" + uid + "/" + rule);
+                    "setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
         }
         try {
             if (chain == FIREWALL_CHAIN_DOZABLE) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3d34976..16b5fb1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -682,7 +682,14 @@
             return mBuffer.descendingIterator();
         }
 
-        public StatusBarNotification[] getArray(int count, boolean includeSnoozed) {
+        public StatusBarNotification[] getArray(UserManager um, int count, boolean includeSnoozed) {
+            ArrayList<Integer> currentUsers = new ArrayList<>();
+            currentUsers.add(UserHandle.USER_ALL);
+            Binder.withCleanCallingIdentity(() -> {
+                for (int user : um.getProfileIds(ActivityManager.getCurrentUser(), false)) {
+                    currentUsers.add(user);
+                }
+            });
             synchronized (mBufferLock) {
                 if (count == 0) count = mBufferSize;
                 List<StatusBarNotification> a = new ArrayList();
@@ -691,8 +698,10 @@
                 while (iter.hasNext() && i < count) {
                     Pair<StatusBarNotification, Integer> pair = iter.next();
                     if (pair.second != REASON_SNOOZED || includeSnoozed) {
-                        i++;
-                        a.add(pair.first);
+                        if (currentUsers.contains(pair.first.getUserId())) {
+                            i++;
+                            a.add(pair.first);
+                        }
                     }
                 }
                 return a.toArray(new StatusBarNotification[a.size()]);
@@ -4200,22 +4209,32 @@
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
                     "NotificationManagerService.getActiveNotifications");
 
-            StatusBarNotification[] tmp = null;
+            ArrayList<StatusBarNotification> tmp = new ArrayList<>();
             int uid = Binder.getCallingUid();
 
+            ArrayList<Integer> currentUsers = new ArrayList<>();
+            currentUsers.add(UserHandle.USER_ALL);
+            Binder.withCleanCallingIdentity(() -> {
+                for (int user : mUm.getProfileIds(ActivityManager.getCurrentUser(), false)) {
+                    currentUsers.add(user);
+                }
+            });
+
             // noteOp will check to make sure the callingPkg matches the uid
             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
                     callingAttributionTag, null)
                     == MODE_ALLOWED) {
                 synchronized (mNotificationLock) {
-                    tmp = new StatusBarNotification[mNotificationList.size()];
                     final int N = mNotificationList.size();
-                    for (int i=0; i<N; i++) {
-                        tmp[i] = mNotificationList.get(i).getSbn();
+                    for (int i = 0; i < N; i++) {
+                        final StatusBarNotification sbn = mNotificationList.get(i).getSbn();
+                        if (currentUsers.contains(sbn.getUserId())) {
+                            tmp.add(sbn);
+                        }
                     }
                 }
             }
-            return tmp;
+            return tmp.toArray(new StatusBarNotification[tmp.size()]);
         }
 
         /**
@@ -4324,7 +4343,7 @@
                     callingAttributionTag, null)
                     == MODE_ALLOWED) {
                 synchronized (mArchive) {
-                    tmp = mArchive.getArray(count, includeSnoozed);
+                    tmp = mArchive.getArray(mUm, count, includeSnoozed);
                 }
             }
             return tmp;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 70e968f..66c7c50 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -69,6 +69,8 @@
 import com.android.server.LocalServices;
 import com.android.server.uri.UriGrantsManagerInternal;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
@@ -467,6 +469,7 @@
             rv.getPackage(), rv.getLayoutId(), rv.estimateMemoryUsage(), rv.toString());
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
         final Notification notification = getSbn().getNotification();
         pw.println(prefix + this);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 39ee0f4..a506743 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -29,7 +29,6 @@
 import android.apex.IApexService;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.SigningDetails;
 import android.content.pm.parsing.result.ParseResult;
@@ -40,6 +39,7 @@
 import android.os.ServiceManager;
 import android.os.Trace;
 import android.sysprop.ApexProperties;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.PrintWriterPrinter;
@@ -608,18 +608,21 @@
                             continue;
                         }
 
-                        String name = service.getName();
-                        for (ApexSystemServiceInfo info : mApexSystemServices) {
-                            if (info.getName().equals(name)) {
-                                throw new IllegalStateException(String.format(
-                                        "Duplicate apex-system-service %s from %s, %s",
-                                        name, info.mJarPath, service.getJarPath()));
+                        if (ai.isActive) {
+                            String name = service.getName();
+                            for (int j = 0; j < mApexSystemServices.size(); j++) {
+                                ApexSystemServiceInfo info = mApexSystemServices.get(j);
+                                if (info.getName().equals(name)) {
+                                    throw new IllegalStateException(TextUtils.formatSimple(
+                                            "Duplicate apex-system-service %s from %s, %s", name,
+                                            info.mJarPath, service.getJarPath()));
+                                }
                             }
+                            ApexSystemServiceInfo info = new ApexSystemServiceInfo(
+                                    service.getName(), service.getJarPath(),
+                                    service.getInitOrder());
+                            mApexSystemServices.add(info);
                         }
-
-                        ApexSystemServiceInfo info = new ApexSystemServiceInfo(
-                                service.getName(), service.getJarPath(), service.getInitOrder());
-                        mApexSystemServices.add(info);
                     }
                     Collections.sort(mApexSystemServices);
                     mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
@@ -809,7 +812,7 @@
                 throw new RuntimeException(re);
             } catch (Exception e) {
                 throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+                        PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                         "apexd verification failed : " + e.getMessage());
             }
         }
@@ -836,7 +839,7 @@
                 throw new RuntimeException(re);
             } catch (Exception e) {
                 throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+                        PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                         "Failed to mark apexd session as ready : " + e.getMessage());
             }
         }
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index d745a23..0b0d1458 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -377,6 +377,12 @@
                 + Integer.toHexString(flags) + " migrateAppData=" + migrateAppData);
         List<String> result = onlyCoreApps ? new ArrayList<>() : null;
 
+        try {
+            mInstaller.cleanupInvalidPackageDirs(volumeUuid, userId, flags);
+        } catch (Installer.InstallerException e) {
+            logCriticalInfo(Log.WARN, "Failed to cleanup deleted dirs: " + e);
+        }
+
         final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId);
         final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId);
 
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 0d9ccd2..2a4882a 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -51,6 +51,8 @@
 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.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -98,7 +100,7 @@
  * {@link ComputerEngine} and {@link ComputerLocked}.
  */
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-public interface Computer {
+public interface Computer extends PackageDataSnapshot {
 
     /**
      * Every method must be annotated.
@@ -178,6 +180,18 @@
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
             String resolvedType, int userId);
+
+    /**
+     * Filters out ephemeral activities.
+     * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
+     * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
+     *
+     * @param resolveInfos The pre-filtered list of resolved activities
+     * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
+     *          is performed.
+     * @param intent
+     * @return A filtered list of resolved activities.
+     */
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
@@ -648,4 +662,16 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
     ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @NonNull
+    ComponentResolverApi getComponentResolver();
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    PackageStateInternal getDisabledSystemPackage(@NonNull String packageName);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    ResolveInfo getInstantAppInstallerInfo();
 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index c437697..30ac1b8 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -49,7 +49,6 @@
 
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
@@ -59,6 +58,7 @@
 import static com.android.server.pm.PackageManagerService.HIDE_EPHEMERAL_APIS;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -143,6 +143,7 @@
 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.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationUtils;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -362,7 +363,7 @@
     private final PermissionManagerServiceInternal mPermissionManager;
     private final ApexManager mApexManager;
     private final PackageManagerServiceInjector mInjector;
-    private final ComponentResolver mComponentResolver;
+    private final ComponentResolverApi mComponentResolver;
     private final InstantAppResolverConnection mInstantAppResolverConnection;
     private final DefaultAppProvider mDefaultAppProvider;
     private final DomainVerificationManagerInternal mDomainVerificationManager;
@@ -644,7 +645,7 @@
         // reader
         String pkgName = intent.getPackage();
         if (pkgName == null) {
-            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                     resolvedType, flags, userId);
             if (resolveInfos == null) {
                 return Collections.emptyList();
@@ -654,7 +655,7 @@
         }
         final AndroidPackage pkg = mPackages.get(pkgName);
         if (pkg != null) {
-            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                     resolvedType, flags, pkg.getServices(),
                     userId);
             if (resolveInfos == null) {
@@ -691,7 +692,7 @@
             }
 
             // Check for results in the current profile.
-            result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+            result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
                     intent, resolvedType, flags, userId), userId);
             addInstant = isInstantAppResolutionAllowed(intent, result, userId,
                     false /*skipPackageCheck*/, flags);
@@ -753,7 +754,7 @@
             result = null;
             if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
                     || !shouldFilterApplication(setting, filterCallingUid, userId))) {
-                result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+                result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
                         intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
                         userId), userId);
             }
@@ -1181,7 +1182,7 @@
                 sourceUserId)) {
             return null;
         }
-        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
                 resolvedType, flags, parentUserId);
 
         if (resultTargetUser == null || resultTargetUser.isEmpty()) {
@@ -1232,7 +1233,7 @@
             Intent intent, String resolvedType, int userId) {
         CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
         if (resolver != null) {
-            return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+            return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/, userId);
         }
         return null;
     }
@@ -1447,7 +1448,7 @@
         ResolveInfo localInstantApp = null;
         boolean blockResolution = false;
         if (!alreadyResolvedLocally) {
-            final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
+            final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(this,
                     intent,
                     resolvedType,
                     flags
@@ -1493,8 +1494,8 @@
                                 null /*callingFeatureId*/, isRequesterInstantApp, userId,
                                 null /*verificationBundle*/, resolveForStart,
                                 digest.getDigestPrefixSecure(), token);
-                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
-                        mInstantAppResolverConnection, requestObject);
+                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(this,
+                        mUserManager, mInstantAppResolverConnection, requestObject);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             } else {
                 // we have an instant application locally, but, we can't admit that since
@@ -1830,7 +1831,7 @@
             return null;
         }
 
-        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
                 resolvedType, flags, targetUserId);
         if (CollectionUtils.isEmpty(resultTargetUser)) {
             return null;
@@ -2586,7 +2587,7 @@
                 mSettings.getPersistentPreferredActivities(userId);
         //TODO(b/158003772): Remove double query
         List<PersistentPreferredActivity> pprefs = ppir != null
-                ? ppir.queryIntent(intent, resolvedType,
+                ? ppir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : new ArrayList<>();
@@ -3133,8 +3134,8 @@
                 writer.println("Domain verification status:");
                 writer.increaseIndent();
                 try {
-                    mDomainVerificationManager.printState(writer, packageName,
-                            UserHandle.USER_ALL, mSettings::getPackage);
+                    mDomainVerificationManager.printState(this, writer, packageName,
+                            UserHandle.USER_ALL);
                 } catch (Exception e) {
                     pw.println("Failure printing domain verification information");
                     Slog.e(TAG, "Failure printing domain verification information", e);
@@ -3244,7 +3245,7 @@
         // Get the list of preferred activities that handle the intent
         if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
         List<PreferredActivity> prefs = pir != null
-                ? pir.queryIntent(intent, resolvedType,
+                ? pir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : null;
@@ -3376,7 +3377,7 @@
                                         pa.mPref.mComponent,
                                         pa.mPref.mAlways);
                                 pir.removeFilter(pa);
-                                pir.addFilter(freshPa);
+                                pir.addFilter(this, freshPa);
                                 result.mChanged = true;
                             } else {
                                 if (DEBUG_PREFERRED) {
@@ -3399,7 +3400,7 @@
                                 PreferredActivity lastChosen = new PreferredActivity(
                                         pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
                                         false);
-                                pir.addFilter(lastChosen);
+                                pir.addFilter(this, lastChosen);
                                 result.mChanged = true;
                             }
                             result.mPreferredResolveInfo = null;
@@ -3454,7 +3455,7 @@
             Slog.v(TAG, "Looking for persistent preferred activities...");
         }
         List<PersistentPreferredActivity> pprefs = ppir != null
-                ? ppir.queryIntent(intent, resolvedType,
+                ? ppir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : null;
@@ -4661,7 +4662,8 @@
             int callingUid) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
-        final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
+        final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
+                userId);
         boolean checkedGrants = false;
         if (providerInfo != null) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -4739,7 +4741,7 @@
         final List<String> names = new ArrayList<>();
         final List<ProviderInfo> infos = new ArrayList<>();
         final int callingUserId = UserHandle.getCallingUserId();
-        mComponentResolver.querySyncProviders(names, infos, safeMode, callingUserId);
+        mComponentResolver.querySyncProviders(this, names, infos, safeMode, callingUserId);
         for (int i = infos.size() - 1; i >= 0; i--) {
             final ProviderInfo providerInfo = infos.get(i);
             final PackageStateInternal ps = mSettings.getPackage(providerInfo.packageName);
@@ -4771,8 +4773,8 @@
         if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForComponent(flags, userId);
         ArrayList<ProviderInfo> finalList = null;
-        final List<ProviderInfo> matchList =
-                mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
+        final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
+                metaDataKey, uid, flags, userId);
         final int listSize = (matchList == null ? 0 : matchList.size());
         for (int i = 0; i < listSize; i++) {
             final ProviderInfo providerInfo = matchList.get(i);
@@ -5674,4 +5676,22 @@
     public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
         return mSettings.getSharedUserPackages(sharedUserAppId);
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        return mComponentResolver;
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        return mSettings.getDisabledSystemPkg(packageName);
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        return mInstantAppInstallerInfo;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 583348b..5d89c7d 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -48,6 +48,7 @@
 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.pm.resolution.ComponentResolverApi;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -868,4 +869,28 @@
             return super.getSharedUserPackages(sharedUserAppId);
         }
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        synchronized (mLock) {
+            return super.getComponentResolver();
+        }
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        synchronized (mLock) {
+            return super.getDisabledSystemPackage(packageName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        synchronized (mLock) {
+            return super.getInstantAppInstallerInfo();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 72e67da..216ad71 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -49,6 +49,7 @@
 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.pm.resolution.ComponentResolverApi;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -1299,4 +1300,28 @@
             return current.mComputer.getSharedUserPackages(sharedUserAppId);
         }
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getComponentResolver();
+        }
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getDisabledSystemPackage(packageName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getInstantAppInstallerInfo();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3220b31..58f9bb2c 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -453,7 +453,8 @@
         }
         for (final int affectedUserId : affectedUserIds) {
             if (hadSuspendAppsPermission.get(affectedUserId)) {
-                mPm.unsuspendForSuspendingPackage(packageName, affectedUserId);
+                mPm.unsuspendForSuspendingPackage(mPm.snapshotComputer(), packageName,
+                        affectedUserId);
                 mPm.removeAllDistractingPackageRestrictions(affectedUserId);
             }
         }
diff --git a/services/core/java/com/android/server/pm/DomainVerificationConnection.java b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
index d24435e..db8c6dc 100644
--- a/services/core/java/com/android/server/pm/DomainVerificationConnection.java
+++ b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
@@ -26,17 +26,12 @@
 import android.os.Message;
 import android.os.UserHandle;
 
-import com.android.internal.util.FunctionalUtils;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
 
-import java.util.function.Consumer;
-import java.util.function.Function;
-
 public final class DomainVerificationConnection implements DomainVerificationService.Connection,
         DomainVerificationProxyV1.Connection, DomainVerificationProxyV2.Connection {
     final PackageManagerService mPm;
@@ -111,42 +106,8 @@
         return mUmInternal.exists(userId);
     }
 
-    @Override
-    public void withPackageSettingsSnapshot(
-            @NonNull Consumer<Function<String, PackageStateInternal>> block) {
-        mPmInternal.withPackageSettingsSnapshot(block);
-    }
-
-    @Override
-    public <Output> Output withPackageSettingsSnapshotReturning(
-            @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
-                    Output> block) {
-        return mPmInternal.withPackageSettingsSnapshotReturning(block);
-    }
-
-    @Override
-    public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
-            @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageStateInternal>,
-                    ExceptionType> block) throws ExceptionType {
-        mPmInternal.withPackageSettingsSnapshotThrowing(block);
-    }
-
-    @Override
-    public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
-            withPackageSettingsSnapshotThrowing2(
-                    @NonNull FunctionalUtils.ThrowingChecked2Consumer<
-                            Function<String, PackageStateInternal>, ExceptionOne,
-                            ExceptionTwo> block)
-            throws ExceptionOne, ExceptionTwo {
-        mPmInternal.withPackageSettingsSnapshotThrowing2(block);
-    }
-
-    @Override
-    public <Output, ExceptionType extends Exception> Output
-            withPackageSettingsSnapshotReturningThrowing(
-            @NonNull FunctionalUtils.ThrowingCheckedFunction<
-                    Function<String, PackageStateInternal>, Output, ExceptionType> block)
-            throws ExceptionType {
-        return mPmInternal.withPackageSettingsSnapshotReturningThrowing(block);
+    @NonNull
+    public Computer snapshot() {
+        return (Computer) mPmInternal.snapshot();
     }
 }
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 5ab0c4c..2b46ed4 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -37,6 +37,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.function.BiConsumer;
@@ -51,6 +53,7 @@
         mPm = pm;
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     public void doDump(FileDescriptor fd, PrintWriter pw, String[] args) {
         DumpState dumpState = new DumpState();
         ArraySet<String> permissionNames = null;
@@ -402,7 +405,8 @@
         }
 
         if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
-            mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
+            mPm.mComponentResolver.dumpContentProviders(mPm.snapshotComputer(), pw, dumpState,
+                    packageName);
         }
 
         if (!checkin
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2ef092e..1fc291d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -144,6 +144,7 @@
 import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.security.VerityUtils;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.pm.dex.ArtManagerService;
@@ -182,7 +183,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
@@ -454,7 +454,8 @@
             KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
             ksms.addScannedPackageLPw(pkg);
 
-            mPm.mComponentResolver.addAllComponents(pkg, chatty);
+            mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage,
+                    mPm.snapshotComputer());
             mPm.mAppsFilter.addPackage(pkgSetting, isReplace);
             mPm.addAllPackageProperties(pkg);
 
@@ -1594,18 +1595,16 @@
                         parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
                     }
 
-                    // Check for shared user id changes
-                    if (!Objects.equals(oldPackage.getSharedUserId(),
-                            parsedPackage.getSharedUserId())
-                            // Don't mark as invalid if the app is trying to
-                            // leave a sharedUserId
-                            && parsedPackage.getSharedUserId() != null) {
+                    // APK should not change its sharedUserId declarations
+                    final var oldSharedUid = oldPackage.getSharedUserId() != null
+                            ? oldPackage.getSharedUserId() : "<nothing>";
+                    final var newSharedUid = parsedPackage.getSharedUserId() != null
+                            ? parsedPackage.getSharedUserId() : "<nothing>";
+                    if (!oldSharedUid.equals(newSharedUid)) {
                         throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
                                 "Package " + parsedPackage.getPackageName()
                                         + " shared user changed from "
-                                        + (oldPackage.getSharedUserId() != null
-                                        ? oldPackage.getSharedUserId() : "<nothing>")
-                                        + " to " + parsedPackage.getSharedUserId());
+                                        + oldSharedUid + " to " + newSharedUid);
                     }
 
                     // In case of rollback, remember per-user/profile install state
@@ -2561,7 +2560,9 @@
         Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
 
         synchronized (mPm.mLock) {
-            size = mPm.mPendingBroadcasts.size();
+            final SparseArray<ArrayMap<String, ArrayList<String>>> userIdToPackagesToComponents =
+                    mPm.mPendingBroadcasts.copiedMap();
+            size = userIdToPackagesToComponents.size();
             if (size <= 0) {
                 // Nothing to be done. Just return
                 return;
@@ -2571,11 +2572,11 @@
             uids = new int[size];
             int i = 0;  // filling out the above arrays
 
-            for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) {
-                final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n);
+            for (int n = 0; n < size; n++) {
+                final int packageUserId = userIdToPackagesToComponents.keyAt(n);
                 final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
-                        mPm.mPendingBroadcasts.packagesForUserId(packageUserId);
-                final int numComponents = componentsToBroadcast.size();
+                        userIdToPackagesToComponents.valueAt(n);
+                final int numComponents = CollectionUtils.size(componentsToBroadcast);
                 for (int index = 0; i < size && index < numComponents; index++) {
                     packages[i] = componentsToBroadcast.keyAt(index);
                     components[i] = componentsToBroadcast.valueAt(index);
@@ -2885,9 +2886,13 @@
             }
         }
 
-        final boolean deferInstallObserver = succeeded && update && !killApp;
+        final boolean deferInstallObserver = succeeded && update;
         if (deferInstallObserver) {
-            mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+            if (killApp) {
+                mPm.scheduleDeferredPendingKillInstallObserver(res, installObserver);
+            } else {
+                mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+            }
         } else {
             mPm.notifyInstallObserver(res, installObserver);
         }
@@ -3692,10 +3697,13 @@
             }
             disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
                     parsedPackage.getPackageName());
-            sharedUserSetting = (parsedPackage.getSharedUserId() != null)
-                    ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
-                    0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
-                    : null;
+            if (parsedPackage.getSharedUserId() != null && !parsedPackage.isLeavingSharedUid()) {
+                sharedUserSetting = mPm.mSettings.getSharedUserLPw(
+                        parsedPackage.getSharedUserId(),
+                        0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+            } else {
+                sharedUserSetting = null;
+            }
             if (DEBUG_PACKAGE_SCANNING
                     && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
                     && sharedUserSetting != null) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c4389a7..742cc02 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -357,6 +357,20 @@
         }
     }
 
+    /**
+     * Remove all invalid dirs under app data folder.
+     * All dirs are supposed to be valid file and package names.
+     */
+    public void cleanupInvalidPackageDirs(String uuid, int userId, int flags)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.cleanupInvalidPackageDirs(uuid, userId, flags);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void moveCompleteApp(String fromUuid, String toUuid, String packageName,
             int appId, String seInfo, int targetSdkVersion,
             String fromCodePath) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 79f8dc1..92d6a82 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -57,6 +57,7 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
 import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
+import com.android.server.pm.resolution.ComponentResolver;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -134,8 +135,9 @@
         }
     }
 
-    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
-            InstantAppResolverConnection connection, InstantAppRequest requestObj) {
+    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(@NonNull Computer computer,
+            @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+            InstantAppRequest requestObj) {
         final long startTime = System.currentTimeMillis();
         final String token = requestObj.token;
         if (DEBUG_INSTANT) {
@@ -149,7 +151,7 @@
             final List<InstantAppResolveInfo> instantAppResolveInfoList =
                     connection.getInstantAppResolveInfoList(buildRequestInfo(requestObj));
             if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
-                resolveInfo = InstantAppResolver.filterInstantAppIntent(
+                resolveInfo = InstantAppResolver.filterInstantAppIntent(computer, userManager,
                         instantAppResolveInfoList, origIntent, requestObj.resolvedType,
                         requestObj.userId, origIntent.getPackage(), token,
                         requestObj.hostDigestPrefixSecure);
@@ -187,9 +189,10 @@
         return resolveInfo;
     }
 
-    public static void doInstantAppResolutionPhaseTwo(Context context,
-            InstantAppResolverConnection connection, InstantAppRequest requestObj,
-            ActivityInfo instantAppInstaller, Handler callbackHandler) {
+    public static void doInstantAppResolutionPhaseTwo(Context context, @NonNull Computer computer,
+            @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+            InstantAppRequest requestObj, ActivityInfo instantAppInstaller,
+            Handler callbackHandler) {
         final long startTime = System.currentTimeMillis();
         final String token = requestObj.token;
         if (DEBUG_INSTANT) {
@@ -205,7 +208,7 @@
                 final Intent failureIntent;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
-                            InstantAppResolver.filterInstantAppIntent(
+                            InstantAppResolver.filterInstantAppIntent(computer, userManager,
                                     instantAppResolveInfoList, origIntent, null /*resolvedType*/,
                                     0 /*userId*/, origIntent.getPackage(),
                                     token, requestObj.hostDigestPrefixSecure);
@@ -386,7 +389,8 @@
         );
     }
 
-    private static AuxiliaryResolveInfo filterInstantAppIntent(
+    private static AuxiliaryResolveInfo filterInstantAppIntent(@NonNull Computer computer,
+            @NonNull UserManagerService userManager,
             List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent,
             String resolvedType, int userId, String packageName, String token,
             int[] hostDigestPrefixSecure) {
@@ -421,7 +425,8 @@
             }
             // We matched a resolve info; resolve the filters to see if anything matches completely.
             List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
-                    origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
+                    computer, userManager, origIntent, resolvedType, userId, packageName, token,
+                    instantAppResolveInfo);
             if (matchFilters != null) {
                 if (matchFilters.isEmpty()) {
                     requiresSecondPhase = true;
@@ -464,7 +469,8 @@
      *
      */
     private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
-            Intent origIntent, String resolvedType, int userId, String packageName, String token,
+            @NonNull Computer computer, @NonNull UserManagerService userManager, Intent origIntent,
+            String resolvedType, int userId, String packageName, String token,
             InstantAppResolveInfo instantAppInfo) {
         if (instantAppInfo.shouldLetInstallerDecide()) {
             return Collections.singletonList(
@@ -490,7 +496,7 @@
             return Collections.emptyList();
         }
         final ComponentResolver.InstantAppIntentResolver instantAppResolver =
-                new ComponentResolver.InstantAppIntentResolver();
+                new ComponentResolver.InstantAppIntentResolver(userManager);
         for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
             final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
             final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
@@ -508,7 +514,7 @@
                         && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
                     continue;
                 }
-                instantAppResolver.addFilter(
+                instantAppResolver.addFilter(computer,
                         new AuxiliaryResolveInfo.AuxiliaryFilter(
                                 filter,
                                 instantAppInfo,
@@ -518,8 +524,8 @@
             }
         }
         List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
-                instantAppResolver.queryIntent(
-                        origIntent, resolvedType, false /*defaultOnly*/, userId);
+                instantAppResolver.queryIntent(computer, origIntent, resolvedType,
+                        false /*defaultOnly*/, userId);
         if (!matchedResolveInfoList.isEmpty()) {
             if (DEBUG_INSTANT) {
                 Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index ec71940..e8faca9 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -24,6 +24,7 @@
 import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
 import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
 import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
+import static com.android.server.pm.PackageManagerService.DEFERRED_PENDING_KILL_INSTALL_OBSERVER;
 import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
 import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
@@ -126,10 +127,12 @@
                     }
                 }
             } break;
-            case DEFERRED_NO_KILL_INSTALL_OBSERVER: {
-                String packageName = (String) msg.obj;
+            case DEFERRED_NO_KILL_INSTALL_OBSERVER:
+            case DEFERRED_PENDING_KILL_INSTALL_OBSERVER: {
+                final String packageName = (String) msg.obj;
                 if (packageName != null) {
-                    mPm.notifyInstallObserver(packageName);
+                    final boolean killApp = msg.what == DEFERRED_PENDING_KILL_INSTALL_OBSERVER;
+                    mPm.notifyInstallObserver(packageName, killApp);
                 }
             } break;
             case WRITE_SETTINGS: {
@@ -313,6 +316,8 @@
             }
             case INSTANT_APP_RESOLUTION_PHASE_TWO: {
                 InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
+                        mPm.snapshotComputer(),
+                        mPm.mUserManager,
                         mPm.mInstantAppResolverConnection,
                         (InstantAppRequest) msg.obj,
                         mPm.mInstantAppInstallerActivity,
@@ -380,6 +385,7 @@
             case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
                 try {
                     mPm.mInjector.getSharedLibrariesImpl().pruneUnusedStaticSharedLibraries(
+                            mPm.snapshotComputer(),
                             Long.MAX_VALUE,
                             Settings.Global.getLong(mPm.mContext.getContentResolver(),
                                     Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fe2fe09..2973c82 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -334,7 +334,7 @@
                 StagingManager.StagedSession stagedSession = session.mStagedSession;
                 if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
                         && getSession(stagedSession.getParentSessionId()) == null) {
-                    stagedSession.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                    stagedSession.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
                             "An orphan staged session " + stagedSession.sessionId() + " is found, "
                                 + "parent " + stagedSession.getParentSessionId() + " is missing");
                     continue;
@@ -852,7 +852,7 @@
                 mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
-                false, false, false, SessionInfo.SESSION_NO_ERROR, "");
+                false, false, false, PackageManager.INSTALL_UNKNOWN, "");
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d230004..3cf0c14 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -82,7 +82,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -462,23 +461,23 @@
     @GuardedBy("mLock")
     private boolean mSessionFailed;
     @GuardedBy("mLock")
-    private int mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+    private int mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
     @GuardedBy("mLock")
     private String mSessionErrorMessage;
 
     @Nullable
     final StagedSession mStagedSession;
 
+    /**
+     * The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
+     * to delay session clean-up until it is safe to do so.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    private Runnable mPendingAbandonCallback;
+
     @VisibleForTesting
     public class StagedSession implements StagingManager.StagedSession {
-        /**
-         * The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
-         * to delay session clean-up until it is safe to do so.
-         */
-        @GuardedBy("mLock")
-        @Nullable
-        private Runnable mPendingAbandonCallback;
-
         @Override
         public List<StagingManager.StagedSession> getChildSessions() {
             if (!params.isMultiPackage) {
@@ -575,9 +574,7 @@
 
         @Override
         public boolean isInTerminalState() {
-            synchronized (mLock) {
-                return mSessionApplied || mSessionFailed;
-            }
+            return PackageInstallerSession.this.isInTerminalState();
         }
 
         @Override
@@ -612,48 +609,7 @@
 
         @Override
         public void abandon() {
-            final Runnable r;
-            synchronized (mLock) {
-                assertNotChild("StagedSession#abandon");
-                assertCallerIsOwnerOrRootOrSystem();
-                if (isInTerminalState()) {
-                    // We keep the session in the database if it's in a finalized state. It will be
-                    // removed by PackageInstallerService when the last update time is old enough.
-                    // Also, in such cases cleanStageDir() has already been executed so no need to
-                    // do it now.
-                    return;
-                }
-                mDestroyed = true;
-                r = () -> {
-                    assertNotLocked("abandonStaged");
-                    if (mCommitted.get()) {
-                        mStagingManager.abortCommittedSession(this);
-                    }
-                    destroy();
-                    dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
-                    maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
-                            "Session was abandoned because the parent session is abandoned");
-                };
-                if (mStageDirInUse) {
-                    // Pre-reboot verification is ongoing, not safe to clean up the session yet.
-                    mPendingAbandonCallback = r;
-                    mCallback.onSessionChanged(PackageInstallerSession.this);
-                    return;
-                }
-            }
-            r.run();
-        }
-
-        /**
-         * Called when pre-reboot verification has ended.
-         * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
-         */
-        private void notifyEndPreRebootVerification() {
-            synchronized (mLock) {
-                Preconditions.checkState(mStageDirInUse);
-                mStageDirInUse = false;
-            }
-            dispatchPendingAbandonCallback();
+            PackageInstallerSession.this.abandon();
         }
 
         /**
@@ -669,17 +625,6 @@
             Preconditions.checkArgument(!isInTerminalState());
             verify();
         }
-
-        private void dispatchPendingAbandonCallback() {
-            final Runnable callback;
-            synchronized (mLock) {
-                callback = mPendingAbandonCallback;
-                mPendingAbandonCallback = null;
-            }
-            if (callback != null) {
-                callback.run();
-            }
-        }
     }
 
     /**
@@ -1138,9 +1083,15 @@
         }
     }
 
+    private boolean isInTerminalState() {
+        synchronized (mLock) {
+            return mSessionApplied || mSessionFailed;
+        }
+    }
+
     /** Returns true if a staged session has reached a final state and can be forgotten about  */
     public boolean isStagedAndInTerminalState() {
-        return params.isStaged && mStagedSession.isInTerminalState();
+        return params.isStaged && isInTerminalState();
     }
 
     private void assertNotLocked(String cookie) {
@@ -1968,9 +1919,6 @@
 
     private void onSessionVerificationFailure(int error, String msg) {
         Slog.e(TAG, "Failed to verify session " + sessionId);
-        if (isStaged()) {
-            mStagedSession.notifyEndPreRebootVerification();
-        }
         // Dispatch message to remove session from PackageInstallerService.
         dispatchSessionFinished(error, msg, null);
         maybeFinishChildSessions(error, msg);
@@ -2279,6 +2227,17 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void markStageDirInUseLocked() throws PackageManagerException {
+        if (mDestroyed) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session destroyed");
+        }
+        // Set this flag to prevent abandon() from deleting staging files when verification or
+        // installation is about to start.
+        mStageDirInUse = true;
+    }
+
     private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
         synchronized (mLock) {
             if (mStageDirInUse) {
@@ -2324,19 +2283,14 @@
     private void verifyNonStaged()
             throws PackageManagerException {
         synchronized (mLock) {
-            if (mDestroyed) {
-                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                        "Session destroyed");
-            }
-            // Set this flag to prevent abandon() from deleting staging files while verification is
-            // in progress. For staged sessions, we will reset this flag when verification is done
-            // so abandon() can take effect. For non-staged sessions, the staging files will be
-            // deleted when install is completed (no matter success or not). No need to reset
-            // the flag.
-            mStageDirInUse = true;
+            markStageDirInUseLocked();
         }
         mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
             mHandler.post(() -> {
+                if (dispatchPendingAbandonCallback()) {
+                    // No need to continue if abandoned
+                    return;
+                }
                 if (error == INSTALL_SUCCEEDED) {
                     onVerificationComplete();
                 } else {
@@ -2360,7 +2314,7 @@
                 maybeFinishChildSessions(INSTALL_SUCCEEDED, "Session installed");
             } else {
                 PackageManagerException e = (PackageManagerException) t.getCause();
-                setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                setSessionFailed(e.error,
                         PackageManager.installStatusToString(e.error, e.getMessage()));
                 dispatchSessionFinished(e.error, e.getMessage(), null);
                 maybeFinishChildSessions(e.error, e.getMessage());
@@ -2426,7 +2380,6 @@
     @WorkerThread
     private void onVerificationComplete() {
         if (isStaged()) {
-            mStagedSession.notifyEndPreRebootVerification();
             mStagingManager.commitSession(mStagedSession);
             sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
             return;
@@ -2444,14 +2397,11 @@
     private InstallParams makeInstallParams(CompletableFuture<Void> future)
             throws PackageManagerException {
         synchronized (mLock) {
-            if (mDestroyed) {
-                throw new PackageManagerException(
-                        INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
-            }
             if (!mSealed) {
                 throw new PackageManagerException(
                         INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
             }
+            markStageDirInUseLocked();
         }
 
         if (isMultiPackage()) {
@@ -3523,22 +3473,6 @@
         }
     }
 
-    private void abandonNonStaged() {
-        synchronized (mLock) {
-            assertNotChild("abandonNonStaged");
-            assertCallerIsOwnerOrRootOrSystem();
-            if (mStageDirInUse) {
-                if (LOGD) Slog.d(TAG, "Ignoring abandon for staging files are in use");
-                return;
-            }
-            mDestroyed = true;
-        }
-        destroy();
-        dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
-        maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
-                "Session was abandoned because the parent session is abandoned");
-    }
-
     private void assertNotChild(String cookie) {
         if (hasParentSessionId()) {
             throw new IllegalStateException(cookie + " can't be called on a child session, id="
@@ -3546,13 +3480,56 @@
         }
     }
 
+    /**
+     * Called when verification has completed. Now it is safe to clean up the session
+     * if {@link #abandon()} has been called previously.
+     *
+     * @return True if this session has been abandoned.
+     */
+    private boolean dispatchPendingAbandonCallback() {
+        final Runnable callback;
+        synchronized (mLock) {
+            Preconditions.checkState(mStageDirInUse);
+            mStageDirInUse = false;
+            callback = mPendingAbandonCallback;
+            mPendingAbandonCallback = null;
+        }
+        if (callback != null) {
+            callback.run();
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public void abandon() {
-        if (params.isStaged) {
-            mStagedSession.abandon();
-        } else {
-            abandonNonStaged();
+        final Runnable r;
+        synchronized (mLock) {
+            assertNotChild("abandon");
+            assertCallerIsOwnerOrRootOrSystem();
+            if (isInTerminalState()) {
+                // Finalized sessions have been properly cleaned up. No need to abandon them.
+                return;
+            }
+            mDestroyed = true;
+            r = () -> {
+                assertNotLocked("abandonStaged");
+                if (isStaged() && mCommitted.get()) {
+                    mStagingManager.abortCommittedSession(mStagedSession);
+                }
+                destroy();
+                dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
+                maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
+                        "Session was abandoned because the parent session is abandoned");
+            };
+            if (mStageDirInUse) {
+                // Verification is ongoing, not safe to clean up the session yet.
+                mPendingAbandonCallback = r;
+                mCallback.onSessionChanged(this);
+                return;
+            }
         }
+        r.run();
     }
 
     @Override
@@ -4045,7 +4022,7 @@
             mSessionReady = true;
             mSessionApplied = false;
             mSessionFailed = false;
-            mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+            mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
             mSessionErrorMessage = "";
         }
         mCallback.onSessionChanged(this);
@@ -4073,7 +4050,7 @@
             mSessionReady = false;
             mSessionApplied = true;
             mSessionFailed = false;
-            mSessionErrorCode = SessionInfo.SESSION_NO_ERROR;
+            mSessionErrorCode = INSTALL_SUCCEEDED;
             mSessionErrorMessage = "";
             Slog.d(TAG, "Marking session " + sessionId + " as applied");
         }
@@ -4103,7 +4080,6 @@
     }
 
     /** {@hide} */
-    @SessionErrorCode
     int getSessionErrorCode() {
         synchronized (mLock) {
             return mSessionErrorCode;
@@ -4591,7 +4567,7 @@
         final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
         final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
         final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
-                SessionInfo.SESSION_NO_ERROR);
+                PackageManager.INSTALL_UNKNOWN);
         final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
 
         if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8f4cef9..4291239 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,6 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageManager.PropertyLocation;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackagePartitions;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
@@ -242,6 +241,8 @@
 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.resolution.ComponentResolver;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -292,7 +293,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -791,9 +791,6 @@
     private final AtomicInteger mNextMoveId = new AtomicInteger();
     final MovePackageHelper.MoveCallbacks mMoveCallbacks;
 
-    // Cache of users who need badging.
-    private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
-
     /**
      * Token for keys in mPendingVerification.
      * Handler thread only!
@@ -843,6 +840,9 @@
     private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
             mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
 
+    private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+            mPendingKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
+
     // Internal interface for permission manager
     final PermissionManagerServiceInternal mPermissionManager;
 
@@ -890,9 +890,11 @@
     static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
     static final int DOMAIN_VERIFICATION = 27;
     static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
+    static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;
 
     static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
     private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
+    private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000;
 
     static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds
 
@@ -912,6 +914,8 @@
 
     final UserManagerService mUserManager;
 
+    final UserNeedsBadgingCache mUserNeedsBadging;
+
     // Stores a list of users whose package restrictions file needs to be updated
     final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
 
@@ -992,7 +996,7 @@
         public final ApplicationInfo androidApplication;
         public final String appPredictionServicePackage;
         public final AppsFilter appsFilter;
-        public final ComponentResolver componentResolver;
+        public final ComponentResolverApi componentResolver;
         public final PackageManagerService service;
         public final WatchedArrayMap<String, Integer> frozenPackages;
         public final SharedLibrariesRead sharedLibraries;
@@ -1081,17 +1085,10 @@
     private final SnapshotStatistics mSnapshotStatistics;
 
     /**
-     * Return the live computer.
-     */
-    Computer liveComputer() {
-        return mLiveComputer;
-    }
-
-    /**
      * Return the cached computer.  The method will rebuild the cached computer if necessary.
      * The live computer will be returned if snapshots are disabled.
      */
-    Computer snapshotComputer() {
+    public Computer snapshotComputer() {
         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.
@@ -1171,16 +1168,17 @@
 
     @Override
     public void notifyPackagesReplacedReceived(String[] packages) {
-        ArraySet<String> packagesToNotify =
-                mComputer.getNotifyPackagesForReplacedReceived(packages);
+        Computer computer = snapshotComputer();
+        ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
         for (int index = 0; index < packagesToNotify.size(); index++) {
-            notifyInstallObserver(packagesToNotify.valueAt(index));
+            notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */);
         }
     }
 
-    void notifyInstallObserver(String packageName) {
-        Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
-                mNoKillInstallObservers.remove(packageName);
+    void notifyInstallObserver(String packageName, boolean killApp) {
+        final Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
+                killApp ? mPendingKillInstallObservers.remove(packageName)
+                        : mNoKillInstallObservers.remove(packageName);
 
         if (pair != null) {
             notifyInstallObserver(pair.first, pair.second);
@@ -1219,6 +1217,15 @@
                 delay ? getPruneUnusedSharedLibrariesDelay() : 0);
     }
 
+    void scheduleDeferredPendingKillInstallObserver(PackageInstalledInfo info,
+            IPackageInstallObserver2 observer) {
+        final String packageName = info.mPkg.getPackageName();
+        mPendingKillInstallObservers.put(packageName, Pair.create(info, observer));
+        final Message message = mHandler.obtainMessage(DEFERRED_PENDING_KILL_INSTALL_OBSERVER,
+                packageName);
+        mHandler.sendMessageDelayed(message, DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS);
+    }
+
     private static long getPruneUnusedSharedLibrariesDelay() {
         return SystemProperties.getLong("debug.pm.prune_unused_shared_libraries_delay",
                 PRUNE_UNUSED_SHARED_LIBRARIES_DELAY);
@@ -1441,7 +1448,7 @@
                 context, lock, installer, installLock, new PackageAbiHelperImpl(),
                 backgroundHandler,
                 SYSTEM_PARTITIONS,
-                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
+                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
                 (i, pm) -> PermissionManagerService.create(context,
                         i.getSystemConfig().getAvailableFeatures()),
                 (i, pm) -> new UserManagerService(context, pm,
@@ -1619,6 +1626,7 @@
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
         mUserManager = injector.getUserManagerService();
+        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
         mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
         mHandler = injector.getHandler();
         mSharedLibraries = injector.getSharedLibrariesImpl();
@@ -1744,6 +1752,7 @@
         mTestUtilityService = LocalServices.getService(TestUtilityService.class);
         LocalServices.addService(PackageManagerInternal.class, mPmInternal);
         mUserManager = injector.getUserManagerService();
+        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
         mComponentResolver = injector.getComponentResolver();
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
@@ -1846,7 +1855,9 @@
                 mAppDataHelper);
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
         mPreferredActivityHelper = new PreferredActivityHelper(this);
-        mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+        mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
+                injector.getCompatibility(), mUserManager, mDomainVerificationManager,
+                mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
         mDexOptHelper = new DexOptHelper(this);
         mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
                 mProtectedPackages);
@@ -1862,6 +1873,7 @@
             registerObservers(true);
         }
 
+        Computer computer = mLiveComputer;
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
         // writer
@@ -1900,7 +1912,8 @@
             t.traceEnd();
 
             t.traceBegin("read user settings");
-            mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
+            mFirstBoot = !mSettings.readLPw(mLiveComputer,
+                    mInjector.getUserManagerInternal().getUsers(
                     /* excludePartial= */ true,
                     /* excludeDying= */ false,
                     /* excludePreCreated= */ false));
@@ -1975,12 +1988,12 @@
                     userIds, startTime);
 
             // Resolve the storage manager.
-            mStorageManagerPackage = getStorageManagerPackageName();
+            mStorageManagerPackage = getStorageManagerPackageName(computer);
 
             // Resolve protected action filters. Only the setup wizard is allowed to
             // have a high priority filter for these actions.
-            mSetupWizardPackage = getSetupWizardPackageNameImpl();
-            mComponentResolver.fixProtectedFilterPriorities();
+            mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
+            mComponentResolver.fixProtectedFilterPriorities(mPmInternal.getSetupWizardPackageName());
 
             mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
             mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
@@ -2108,13 +2121,13 @@
                     SystemClock.uptimeMillis());
 
             if (!mOnlyCore) {
-                mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
-                mRequiredInstallerPackage = getRequiredInstallerLPr();
-                mRequiredUninstallerPackage = getRequiredUninstallerLPr();
+                mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+                mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
+                mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
                 ComponentName intentFilterVerifierComponent =
-                        getIntentFilterVerifierComponentNameLPr();
+                        getIntentFilterVerifierComponentNameLPr(computer);
                 ComponentName domainVerificationAgent =
-                        getDomainVerificationAgentComponentNameLPr();
+                        getDomainVerificationAgentComponentNameLPr(computer);
 
                 DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
                         intentFilterVerifierComponent, domainVerificationAgent, mContext,
@@ -2137,7 +2150,7 @@
 
             // PermissionController hosts default permission granting and role management, so it's a
             // critical part of the core system.
-            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
+            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
 
             mSettings.setPermissionControllerVersion(
                     getPackageInfo(mRequiredPermissionControllerPackage, 0,
@@ -2170,7 +2183,8 @@
                 mInstantAppResolverConnection =
                         mInjector.getInstantAppResolverConnection(instantAppResolverComponent);
                 mInstantAppResolverSettingsComponent =
-                        getInstantAppResolverSettingsLPr(instantAppResolverComponent);
+                        getInstantAppResolverSettingsLPr(computer,
+                                instantAppResolverComponent);
             } else {
                 mInstantAppResolverConnection = null;
                 mInstantAppResolverSettingsComponent = null;
@@ -2258,11 +2272,13 @@
                 "persist.pm.mock-upgrade", false /* default */);
     }
 
-    private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
+    @Nullable
+    private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
 
         final List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+                        PACKAGE_MIME_TYPE,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         if (matches.size() == 1) {
@@ -2300,12 +2316,13 @@
         return servicesExtensionPackage;
     }
 
-    private @NonNull String getRequiredInstallerLPr() {
+    private @NonNull String getRequiredInstallerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent,
+                PACKAGE_MIME_TYPE,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.USER_SYSTEM);
         if (matches.size() == 1) {
@@ -2319,14 +2336,14 @@
         }
     }
 
-    private @NonNull String getRequiredUninstallerLPr() {
+    private @NonNull String getRequiredUninstallerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
 
-        final ResolveInfo resolveInfo = resolveIntent(intent, null,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM);
+        final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent,
+                null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, Binder.getCallingUid());
         if (resolveInfo == null ||
                 mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
             throw new RuntimeException("There must be exactly one uninstaller; found "
@@ -2335,11 +2352,11 @@
         return resolveInfo.getComponentInfo().packageName;
     }
 
-    private @NonNull String getRequiredPermissionControllerLPr() {
+    private @NonNull String getRequiredPermissionControllerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.USER_SYSTEM);
         if (matches.size() == 1) {
@@ -2354,11 +2371,13 @@
         }
     }
 
-    private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
+    @NonNull
+    private ComponentName getIntentFilterVerifierComponentNameLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
 
         final List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+                        PACKAGE_MIME_TYPE,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
@@ -2384,10 +2403,10 @@
     }
 
     @Nullable
-    private ComponentName getDomainVerificationAgentComponentNameLPr() {
+    private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer) {
         Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
         List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, null,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
@@ -2496,13 +2515,14 @@
                         | MATCH_DIRECT_BOOT_UNAWARE
                         | Intent.FLAG_IGNORE_EPHEMERAL
                         | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY);
+        final Computer computer = snapshotComputer();
         final Intent intent = new Intent();
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
         List<ResolveInfo> matches = null;
         for (String action : orderedActions) {
             intent.setAction(action);
-            matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+            matches = computer.queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                     resolveFlags, UserHandle.USER_SYSTEM);
             if (matches.isEmpty()) {
                 if (DEBUG_INSTANT) {
@@ -2532,14 +2552,14 @@
         }
     }
 
-    private @Nullable ComponentName getInstantAppResolverSettingsLPr(
+    private @Nullable ComponentName getInstantAppResolverSettingsLPr(@NonNull Computer computer,
             @NonNull ComponentName resolver) {
         final Intent intent =  new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
                 .addCategory(Intent.CATEGORY_DEFAULT)
                 .setPackage(resolver.getPackageName());
         final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
-        List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
-                UserHandle.USER_SYSTEM);
+        List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
+                resolveFlags, UserHandle.USER_SYSTEM);
         if (matches.isEmpty()) {
             return null;
         }
@@ -2836,8 +2856,9 @@
             }
             if (file.getUsableSpace() >= bytes) return;
 
+            Computer computer = snapshotComputer();
             // 5. Consider shared libraries with refcount=0 and age>min cache period
-            if (internalVolume && mSharedLibraries.pruneUnusedStaticSharedLibraries(bytes,
+            if (internalVolume && mSharedLibraries.pruneUnusedStaticSharedLibraries(computer, bytes,
                     android.provider.Settings.Global.getLong(mContext.getContentResolver(),
                             Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                             FREE_STORAGE_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
@@ -2849,14 +2870,12 @@
 
             // 7. Consider installed instant apps unused longer than min cache period
             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)))
-                ) {
+                if (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;
                 }
             }
@@ -2876,14 +2895,12 @@
 
             // 10. Consider instant meta-data (uninstalled apps) older that min cache period
             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)))
-                ) {
+                if (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;
                 }
             }
@@ -2942,27 +2959,6 @@
     }
 
     /**
-     * Update given flags when being used to request {@link PackageInfo}.
-     */
-    private long updateFlagsForPackage(long flags, int userId) {
-        return mComputer.updateFlagsForPackage(flags, userId);
-    }
-
-    /**
-     * Update given flags when being used to request {@link ApplicationInfo}.
-     */
-    private long updateFlagsForApplication(long flags, int userId) {
-        return mComputer.updateFlagsForApplication(flags, userId);
-    }
-
-    /**
-     * Update given flags when being used to request {@link ComponentInfo}.
-     */
-    private long updateFlagsForComponent(long flags, int userId) {
-        return mComputer.updateFlagsForComponent(flags, userId);
-    }
-
-    /**
      * Update given flags when being used to request {@link ResolveInfo}.
      * <p>Instant apps are resolved specially, depending upon context. Minimally,
      * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
@@ -3314,8 +3310,8 @@
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
-                0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
+        return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(), intent, resolvedType,
+                flags, 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
     }
 
     @Override
@@ -3414,8 +3410,8 @@
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
 
-            return new ParceledListSlice<>(
-                    queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+            return new ParceledListSlice<>(snapshotComputer().queryIntentActivitiesInternal(intent,
+                    resolvedType, flags, userId));
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -3429,68 +3425,28 @@
         return mComputer.getInstantAppPackageName(callingUid);
     }
 
-    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return mComputer.queryIntentActivitiesInternal(intent,
-                resolvedType, flags, userId);
-    }
-
-    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
-            @PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
-            boolean resolveForStart, boolean allowDynamicSplits) {
-        return mComputer.queryIntentActivitiesInternal(intent,
-                resolvedType, flags, privateResolveFlags,
-                filterCallingUid, userId, resolveForStart, allowDynamicSplits);
-    }
-
-    private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
-            int parentUserId) {
-        return mComputer.getCrossProfileDomainPreferredLpr(intent,
-                resolvedType, flags, sourceUserId, parentUserId);
-    }
-
-    /**
-     * Filters out ephemeral activities.
-     * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
-     * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
-     *
-     * @param resolveInfos The pre-filtered list of resolved activities
-     * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
-     *          is performed.
-     * @param intent
-     * @return A filtered list of resolved activities.
-     */
-    List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
-            String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
-            boolean resolveForStart, int userId, Intent intent) {
-        return mComputer.applyPostResolutionFilter(resolveInfos,
-                ephemeralPkgName, allowDynamicSplits, filterCallingUid,
-                resolveForStart, userId, intent);
-    }
-
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
             Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
-                caller, specifics, specificTypes, intent, resolvedType, flags, userId));
+                snapshotComputer(), caller, specifics, specificTypes, intent, resolvedType, flags,
+                userId));
     }
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
-                resolvedType, flags, userId, Binder.getCallingUid()));
+        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
+                snapshotComputer(), intent, resolvedType, flags, userId, Binder.getCallingUid()));
     }
 
     @Override
     public ResolveInfo resolveService(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         final int callingUid = Binder.getCallingUid();
-        return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
-                callingUid);
+        return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent, resolvedType,
+                flags, userId, callingUid);
     }
 
     @Override
@@ -3513,7 +3469,7 @@
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
-                intent, resolvedType, flags, userId));
+                snapshotComputer(), intent, resolvedType, flags, userId));
     }
 
     @Override
@@ -3549,8 +3505,8 @@
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
                 false /* checkShell */, "getEphemeralApplications");
 
-        List<InstantAppInfo> instantApps = executeWithConsistentComputerReturning(computer ->
-                mInstantAppRegistry.getInstantApps(computer, userId));
+        Computer computer = snapshotComputer();
+        List<InstantAppInfo> instantApps = mInstantAppRegistry.getInstantApps(computer, userId);
         if (instantApps != null) {
             return new ParceledListSlice<>(instantApps);
         }
@@ -3766,13 +3722,13 @@
     public void notifyPackageUse(String packageName, int reason) {
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        boolean notify = executeWithConsistentComputerReturning(computer -> {
-            if (getInstantAppPackageName(callingUid) != null) {
-                return isCallerSameApp(packageName, callingUid);
-            } else {
-                return !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
-            }
-        });
+        Computer computer = snapshotComputer();
+        final boolean notify;
+        if (getInstantAppPackageName(callingUid) != null) {
+            notify = isCallerSameApp(packageName, callingUid);
+        } else {
+            notify = !isInstantAppInternal(packageName, callingUserId, Process.SYSTEM_UID);
+        }
         if (!notify) {
             return;
         }
@@ -3907,15 +3863,9 @@
         synchronized (mLock) {
             mPackageUsage.writeNow(mSettings.getPackagesLocked());
 
-            // This is the last chance to write out pending restriction settings
-            if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
-                mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-                synchronized (mDirtyUsers) {
-                    for (int userId : mDirtyUsers) {
-                        mSettings.writePackageRestrictionsLPr(userId);
-                    }
-                    mDirtyUsers.clear();
-                }
+            if (mHandler.hasMessages(WRITE_SETTINGS)) {
+                mHandler.removeMessages(WRITE_SETTINGS);
+                writeSettings();
             }
         }
     }
@@ -4317,34 +4267,33 @@
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
 
         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;
-                }
-                final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
-                        .getDistractionFlags();
-                if (restrictionFlags != oldDistractionFlags) {
-                    changedPackagesList.add(packageName);
-                    changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
-                    changesToCommit.add(packageName);
-                }
+        Computer computer = snapshotComputer();
+        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
+                    || computer.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;
+            }
+            final int oldDistractionFlags = packageState.getUserStateOrDefault(userId)
+                    .getDistractionFlags();
+            if (restrictionFlags != oldDistractionFlags) {
+                changedPackagesList.add(packageName);
+                changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                changesToCommit.add(packageName);
+            }
+        }
 
         commitPackageStateMutation(null, mutator -> {
             final int size = changesToCommit.size();
@@ -4403,8 +4352,9 @@
         final int callingUid = Binder.getCallingUid();
         enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
                 "setPackagesSuspendedAsUser");
-        return mSuspendPackageHelper.setPackagesSuspended(packageNames, suspended, appExtras,
-                launcherExtras, dialogInfo, callingPackage, userId, callingUid);
+        return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
+                suspended, appExtras, launcherExtras, dialogInfo, callingPackage, userId,
+                callingUid);
     }
 
     @Override
@@ -4423,11 +4373,12 @@
         return mComputer.isPackageSuspendedForUser(packageName, userId);
     }
 
-    void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
+    void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
+            @UserIdInt int userId) {
         // 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(
+        final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]);
+        mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer,
                 allPackages, suspendingPackage::equals, userId);
     }
 
@@ -4486,7 +4437,7 @@
             throw new SecurityException("Calling uid " + callingUid
                     + " cannot query getUnsuspendablePackagesForUser for user " + userId);
         }
-        return mSuspendPackageHelper.getUnsuspendablePackagesForUser(
+        return mSuspendPackageHelper.getUnsuspendablePackagesForUser(snapshotComputer(),
                 packageNames, userId, callingUid);
     }
 
@@ -4687,7 +4638,7 @@
             return true;
         };
         PackageStateMutator.InitialState initialState = recordInitialState();
-        boolean allowed = executeWithConsistentComputerReturningThrowing(implementation);
+        boolean allowed = implementation.apply(snapshotComputer());
         if (allowed) {
             // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
             //  should find an alternative which avoids any race conditions
@@ -4697,7 +4648,7 @@
                         targetPackage, state -> state.setInstaller(installerPackageName));
                 if (result.isPackagesChanged() || result.isStateChanged()) {
                     synchronized (mPackageStateWriteLock) {
-                        allowed = executeWithConsistentComputerReturningThrowing(implementation);
+                        allowed = implementation.apply(snapshotComputer());
                         if (allowed) {
                             commitPackageStateMutation(null, targetPackage,
                                     state -> state.setInstaller(installerPackageName));
@@ -4747,12 +4698,12 @@
             }
         };
 
-        PackageStateMutator.Result result = executeWithConsistentComputerReturning(implementation);
+        PackageStateMutator.Result result = implementation.apply(snapshotComputer());
         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);
+                result = implementation.apply(snapshotComputer());
             }
         }
 
@@ -5050,7 +5001,7 @@
                     }
                     if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
                             == PERMISSION_GRANTED) {
-                        unsuspendForSuspendingPackage(packageName, userId);
+                        unsuspendForSuspendingPackage(snapshotComputer(), packageName, userId);
                         removeAllDistractingPackageRestrictions(userId);
                         flushPackageRestrictionsAsUserInternalLocked(userId);
                     }
@@ -5142,17 +5093,7 @@
         updateInstantAppInstallerLocked(packageName);
         scheduleWritePackageRestrictions(userId);
 
-        final ArrayList<String> pendingComponents = mPendingBroadcasts.get(userId, packageName);
-        if (pendingComponents == null) {
-            mPendingBroadcasts.put(userId, packageName, updatedComponents);
-        } else {
-            for (int i = 0; i < updatedComponents.size(); i++) {
-                final String updatedComponent = updatedComponents.get(i);
-                if (!pendingComponents.contains(updatedComponent)) {
-                    pendingComponents.add(updatedComponent);
-                }
-            }
-        }
+        mPendingBroadcasts.addComponents(userId, packageName, updatedComponents);
         if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
             mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
         }
@@ -5340,7 +5281,8 @@
         try {
             try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
                 TypedXmlSerializer serializer = Xml.resolveSerializer(output);
-                mDomainVerificationManager.writeSettings(serializer, true, userId);
+                mDomainVerificationManager.writeSettings(snapshotComputer(), serializer, true,
+                        userId);
                 return output.toByteArray();
             }
         } catch (Exception e) {
@@ -5363,7 +5305,7 @@
 
             // User ID input isn't necessary here as it assumes the user integers match and that
             // the only states inside the backup XML are for the target user.
-            mDomainVerificationManager.restoreSettings(parser);
+            mDomainVerificationManager.restoreSettings(snapshotComputer(), parser);
             input.close();
         } catch (Exception e) {
             if (DEBUG_BACKUP) {
@@ -5409,7 +5351,7 @@
                     }
                 }
             }
-            resolver.addFilter(newFilter);
+            resolver.addFilter(snapshotComputer(), newFilter);
         }
         scheduleWritePackageRestrictions(sourceUserId);
     }
@@ -5498,11 +5440,11 @@
         mPreferredActivityHelper.setHomeActivity(comp, userId);
     }
 
-    private @Nullable String getSetupWizardPackageNameImpl() {
+    private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                         | MATCH_DISABLED_COMPONENTS,
                 UserHandle.myUserId());
@@ -5515,10 +5457,10 @@
         }
     }
 
-    private @Nullable String getStorageManagerPackageName() {
+    private @Nullable String getStorageManagerPackageName(@NonNull Computer computer) {
         final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                         | MATCH_DISABLED_COMPONENTS,
                 UserHandle.myUserId());
@@ -5751,48 +5693,48 @@
         int callingUid = Binder.getCallingUid();
         String componentPkgName = componentName.getPackageName();
 
-        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");
-            }
+        Computer computer = snapshotComputer();
 
-            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");
-            }
+        int componentUid = computer.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");
-            }
-            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);
-            }
+        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");
+        }
 
-            if (!mComponentResolver.componentExists(componentName)) {
-                throw new IllegalArgumentException("Component " + componentName + " not found");
-            }
+        int allowedCallerUid = computer.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);
+        }
 
-            Pair<String, Integer> overrideLabelIcon = packageState.getUserStateOrDefault(userId)
-                    .getOverrideLabelIconForComponent(componentName);
+        if (!computer.getComponentResolver().componentExists(componentName)) {
+            throw new IllegalArgumentException("Component " + componentName + " not found");
+        }
 
-            String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first;
-            Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second;
+        Pair<String, Integer> overrideLabelIcon = packageState.getUserStateOrDefault(userId)
+                .getOverrideLabelIconForComponent(componentName);
 
-            return !TextUtils.equals(existingLabel, nonLocalizedLabel)
-                    || !Objects.equals(existingIcon, icon);
-        });
-        if (!changed) {
+        String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first;
+        Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second;
+
+        if (TextUtils.equals(existingLabel, nonLocalizedLabel)
+                && Objects.equals(existingIcon, icon)) {
+            // Nothing changed
             return;
         }
 
@@ -5800,16 +5742,7 @@
                 state -> state.userState(userId)
                         .setComponentLabelIcon(componentName, nonLocalizedLabel, icon));
 
-        ArrayList<String> components = mPendingBroadcasts.get(userId, componentPkgName);
-        if (components == null) {
-            components = new ArrayList<>();
-            mPendingBroadcasts.put(userId, componentPkgName, components);
-        }
-
-        String className = componentName.getClassName();
-        if (!components.contains(className)) {
-            components.add(className);
-        }
+        mPendingBroadcasts.addComponent(userId, componentPkgName, componentName.getClassName());
 
         if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
             mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
@@ -6018,6 +5951,7 @@
         // packageName -> list of components to send broadcasts now
         final ArrayMap<String, ArrayList<String>> sendNowBroadcasts = new ArrayMap<>(targetSize);
         synchronized (mLock) {
+            Computer computer = snapshotComputer();
             boolean scheduleBroadcastMessage = false;
             boolean isSynchronous = false;
             boolean anyChanged = false;
@@ -6029,8 +5963,8 @@
                 // update enabled settings
                 final ComponentEnabledSetting setting = settings.get(i);
                 final String packageName = setting.getPackageName();
-                if (!setEnabledSettingInternalLocked(pkgSettings.get(packageName), setting,
-                        userId, callingPackage)) {
+                if (!setEnabledSettingInternalLocked(computer, pkgSettings.get(packageName),
+                        setting, userId, callingPackage)) {
                     continue;
                 }
                 anyChanged = true;
@@ -6041,26 +5975,18 @@
                 // collect broadcast list for the package
                 final String componentName = setting.isComponent()
                         ? setting.getClassName() : packageName;
-                ArrayList<String> componentList = sendNowBroadcasts.get(packageName);
-                if (componentList == null) {
-                    componentList = mPendingBroadcasts.get(userId, packageName);
-                }
-                final boolean newPackage = componentList == null;
-                if (newPackage) {
-                    componentList = new ArrayList<>();
-                }
-                if (!componentList.contains(componentName)) {
-                    componentList.add(componentName);
-                }
                 if ((setting.getEnabledFlags() & PackageManager.DONT_KILL_APP) == 0) {
+                    ArrayList<String> componentList = sendNowBroadcasts.get(packageName);
+                    componentList = componentList == null ? new ArrayList<>() : componentList;
+                    if (!componentList.contains(componentName)) {
+                        componentList.add(componentName);
+                    }
                     sendNowBroadcasts.put(packageName, componentList);
                     // Purge entry from pending broadcast list if another one exists already
                     // since we are sending one right away.
                     mPendingBroadcasts.remove(userId, packageName);
                 } else {
-                    if (newPackage) {
-                        mPendingBroadcasts.put(userId, packageName, componentList);
-                    }
+                    mPendingBroadcasts.addComponent(userId, packageName, componentName);
                     scheduleBroadcastMessage = true;
                 }
             }
@@ -6103,8 +6029,9 @@
         }
     }
 
-    private boolean setEnabledSettingInternalLocked(PackageSetting pkgSetting,
-            ComponentEnabledSetting setting, int userId, String callingPackage) {
+    private boolean setEnabledSettingInternalLocked(@NonNull Computer computer,
+            PackageSetting pkgSetting, ComponentEnabledSetting setting, @UserIdInt int userId,
+            String callingPackage) {
         final int newState = setting.getEnabledState();
         final String packageName = setting.getPackageName();
         boolean success = false;
@@ -6123,7 +6050,7 @@
                 // This app should not generally be allowed to get disabled by the UI, but
                 // if it ever does, we don't want to end up with some of the user's apps
                 // permanently suspended.
-                unsuspendForSuspendingPackage(packageName, userId);
+                unsuspendForSuspendingPackage(computer, packageName, userId);
                 removeAllDistractingPackageRestrictions(userId);
             }
             success = true;
@@ -6214,11 +6141,8 @@
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
         if (!mUserManager.exists(userId)) return;
         final int callingUid = Binder.getCallingUid();
-        Pair<Boolean, String> wasNotLaunchedAndInstallerPackageName =
-                executeWithConsistentComputerReturningThrowing(computer -> {
-            if (computer.getInstantAppPackageName(callingUid) != null) {
-                return null;
-            }
+        final Computer computer = snapshotComputer();
+        if (computer.getInstantAppPackageName(callingUid) == null) {
             final int permission = mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
             final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
@@ -6233,29 +6157,30 @@
                     true /* requireFullPermission */, true /* checkShell */, "stop package");
 
             final PackageStateInternal packageState = computer.getPackageStateInternal(packageName);
-            final PackageUserState PackageUserState = packageState == null
+            final PackageUserState packageUserState = packageState == null
                     ? null : packageState.getUserStateOrDefault(userId);
-            if (packageState == null
-                    || computer.shouldFilterApplication(packageState, callingUid, userId)
-                    || PackageUserState.isStopped() == stopped) {
-                return null;
-            }
+            if (packageState != null
+                    && !computer.shouldFilterApplication(packageState, callingUid, userId)
+                    && packageUserState.isStopped() != stopped) {
+                boolean wasNotLaunched = packageUserState.isNotLaunched();
+                commitPackageStateMutation(null, packageName, state -> {
+                    PackageUserStateWrite userState = state.userState(userId);
+                    userState.setStopped(stopped);
+                    if (wasNotLaunched) {
+                        userState.setNotLaunched(false);
+                    }
+                });
 
-            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);
+                    final String installerPackageName =
+                            packageState.getInstallSource().installerPackageName;
+                    if (installerPackageName != null) {
+                        notifyFirstLaunch(packageName, installerPackageName, userId);
+                    }
                 }
-            });
 
-            scheduleWritePackageRestrictions(userId);
+                scheduleWritePackageRestrictions(userId);
+            }
         }
 
         // If this would cause the app to leave force-stop, then also make sure to unhibernate the
@@ -6742,7 +6667,7 @@
                 "Only package verification agents can read the verifier device identity");
 
         synchronized (mLock) {
-            return mSettings.getVerifierDeviceIdentityLPw();
+            return mSettings.getVerifierDeviceIdentityLPw(mLiveComputer);
         }
     }
 
@@ -6777,21 +6702,7 @@
     }
 
     boolean userNeedsBadging(int userId) {
-        int index = mUserNeedsBadging.indexOfKey(userId);
-        if (index < 0) {
-            final UserInfo userInfo;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                userInfo = mUserManager.getUserInfo(userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            final boolean b;
-            b = userInfo != null && userInfo.isManagedProfile();
-            mUserNeedsBadging.put(userId, b);
-            return b;
-        }
-        return mUserNeedsBadging.valueAt(index);
+        return mUserNeedsBadging.get(userId);
     }
 
     @Nullable
@@ -7027,16 +6938,14 @@
         }
 
         @Override
-        public PackageSetting getDisabledSystemPackage(@NonNull String packageName) {
-            synchronized (mLock) {
-                return mSettings.getDisabledSystemPkgLPr(packageName);
-            }
+        public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+            return snapshotComputer().getDisabledSystemPackage(packageName);
         }
 
         @Override
         public @Nullable
         String getDisabledSystemPackageName(@NonNull String packageName) {
-            PackageSetting disabledPkgSetting = getDisabledSystemPackage(
+            PackageStateInternal disabledPkgSetting = getDisabledSystemPackage(
                     packageName);
             AndroidPackage disabledPkg = disabledPkgSetting == null
                     ? null : disabledPkgSetting.getPkg();
@@ -7097,15 +7006,16 @@
 
         @Override
         public void removeAllNonSystemPackageSuspensions(int userId) {
-            final String[] allPackages = mComputer.getAllAvailablePackageNames();
-            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(allPackages,
+            final Computer computer = snapshotComputer();
+            final String[] allPackages = computer.getAllAvailablePackageNames();
+            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages,
                     (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
                     userId);
         }
 
         @Override
         public void removeNonSystemPackageSuspensions(String packageName, int userId) {
-            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
+            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(snapshotComputer(),
                     new String[]{packageName},
                     (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
                     userId);
@@ -7176,9 +7086,8 @@
         public List<ResolveInfo> queryIntentActivities(
                 Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
-            return PackageManagerService.this
-                    .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
-                            userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+            return snapshotComputer().queryIntentActivitiesInternal(intent, resolvedType, flags,
+                    userId);
         }
 
         @Override
@@ -7186,7 +7095,7 @@
                 String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
-                    intent, resolvedType, flags, userId, filterCallingUid);
+                    snapshotComputer(), intent, resolvedType, flags, userId, filterCallingUid);
         }
 
         @Override
@@ -7305,29 +7214,29 @@
         @Override
         public void grantImplicitAccess(int userId, Intent intent,
                 int recipientAppId, int visibleUid, boolean direct, boolean retainOnUpdate) {
-            boolean accessGranted = executeWithConsistentComputerReturning(computer -> {
-                final AndroidPackage visiblePackage = computer.getPackage(visibleUid);
-                final int recipientUid = UserHandle.getUid(userId, recipientAppId);
-                if (visiblePackage == null || computer.getPackage(recipientUid) == null) {
-                    return false;
-                }
+            Computer computer = snapshotComputer();
+            final AndroidPackage visiblePackage = computer.getPackage(visibleUid);
+            final int recipientUid = UserHandle.getUid(userId, recipientAppId);
+            if (visiblePackage == null || computer.getPackage(recipientUid) == null) {
+                return;
+            }
 
-                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 false;
-                    }
-                    return mInstantAppRegistry.grantInstantAccess(userId, intent,
-                            recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
-                } else {
-                    return mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
-                            retainOnUpdate);
+            final boolean instantApp = computer.isInstantAppInternal(
+                    visiblePackage.getPackageName(), userId, visibleUid);
+            final boolean accessGranted;
+            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;
                 }
-            });
+                accessGranted = mInstantAppRegistry.grantInstantAccess(userId, intent,
+                        recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
+            } else {
+                accessGranted = mAppsFilter.grantImplicitAccess(recipientUid, visibleUid,
+                        retainOnUpdate);
+            }
 
             if (accessGranted) {
                 ApplicationPackageManager.invalidateGetPackagesForUidCache();
@@ -7343,8 +7252,7 @@
 
         @Override
         public void pruneInstantApps() {
-            executeWithConsistentComputer(computer ->
-                    mInstantAppRegistry.pruneInstantApps(computer));
+            mInstantAppRegistry.pruneInstantApps(snapshotComputer());
         }
 
         @Override
@@ -7429,7 +7337,7 @@
                 @PackageManager.ResolveInfoFlagsBits long flags,
                 @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
                 boolean resolveForStart, int filterCallingUid) {
-            return mResolveIntentHelper.resolveIntentInternal(
+            return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(),
                     intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
                     filterCallingUid);
         }
@@ -7437,8 +7345,8 @@
         @Override
         public ResolveInfo resolveService(Intent intent, String resolvedType,
                 @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
-            return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
-                    callingUid);
+            return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent,
+                    resolvedType, flags, userId, callingUid);
         }
 
         @Override
@@ -7500,6 +7408,12 @@
         }
 
         @Override
+        public void onPackageProcessKilledForUninstall(String packageName) {
+            mHandler.post(() -> PackageManagerService.this.notifyInstallObserver(packageName,
+                    true /* killApp */));
+        }
+
+        @Override
         public SparseArray<String> getAppsWithSharedUserIds() {
             return mComputer.getAppsWithSharedUserIds();
         }
@@ -7752,7 +7666,8 @@
 
         @Override
         public void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
-            PackageManagerService.this.unsuspendForSuspendingPackage(packageName, affectedUser);
+            PackageManagerService.this.unsuspendForSuspendingPackage(snapshotComputer(),
+                    packageName, affectedUser);
         }
 
         @Override
@@ -7816,52 +7731,6 @@
         }
 
         @Override
-        public void withPackageSettingsSnapshot(
-                @NonNull Consumer<Function<String, PackageStateInternal>> block) {
-            executeWithConsistentComputer(computer ->
-                    block.accept(computer::getPackageStateInternal));
-        }
-
-        @Override
-        public <Output> Output withPackageSettingsSnapshotReturning(
-                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
-                        Output> block) {
-            return executeWithConsistentComputerReturning(computer ->
-                    block.apply(computer::getPackageStateInternal));
-        }
-
-        @Override
-        public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
-                @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String,
-                        PackageStateInternal>, ExceptionType> block) throws ExceptionType {
-            executeWithConsistentComputerThrowing(computer ->
-                    block.accept(computer::getPackageStateInternal));
-        }
-
-        @Override
-        public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
-                withPackageSettingsSnapshotThrowing2(
-                        @NonNull FunctionalUtils.ThrowingChecked2Consumer<
-                                Function<String, PackageStateInternal>, ExceptionOne,
-                                ExceptionTwo> block)
-                throws ExceptionOne, ExceptionTwo {
-            executeWithConsistentComputerThrowing2(
-                    (FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
-                            ExceptionTwo>) computer -> block.accept(computer::getPackageStateInternal));
-        }
-
-        @Override
-        public <Output, ExceptionType extends Exception> Output
-                withPackageSettingsSnapshotReturningThrowing(
-                        @NonNull FunctionalUtils.ThrowingCheckedFunction<
-                                Function<String, PackageStateInternal>, Output,
-                                ExceptionType> block)
-                throws ExceptionType {
-            return executeWithConsistentComputerReturningThrowing(computer ->
-                    block.apply(computer::getPackageStateInternal));
-        }
-
-        @Override
         public void reconcileAppsData(int userId, @StorageManager.StorageFlags int flags,
                 boolean migrateAppsData) {
             PackageManagerService.this.mAppDataHelper.reconcileAppsData(userId, flags,
@@ -7893,6 +7762,12 @@
                 @NonNull Consumer<PackageStateMutator> consumer) {
             return PackageManagerService.this.commitPackageStateMutation(state, consumer);
         }
+
+        @NonNull
+        @Override
+        public Computer snapshot() {
+            return snapshotComputer();
+        }
     }
 
     private boolean setEnabledOverlayPackages(@UserIdInt int userId,
@@ -7900,70 +7775,59 @@
             @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
+            Computer computer = snapshotComputer();
+            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 false;
-            } else if (!targetModified) {
-                // Treat non-modification as a successful commit
+            }
+
+            if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+                    newOverlayPaths)) {
                 return true;
             }
 
+            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);
+
             commitPackageStateMutation(null, mutator -> {
                 mutator.forPackage(targetPackageName)
                         .userState(userId)
@@ -8128,36 +7992,6 @@
         forEachPackageState(mComputer.getPackageStates(), actionWrapped);
     }
 
-    // TODO: Make private
-    void executeWithConsistentComputer(
-            @NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
-        consumer.accept(snapshotComputer());
-    }
-
-    private <T> T executeWithConsistentComputerReturning(
-            @NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
-        return function.apply(snapshotComputer());
-    }
-
-    private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
-            @NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
-            throws ExceptionType {
-        consumer.accept(snapshotComputer());
-    }
-
-    private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
-    executeWithConsistentComputerThrowing2(
-            @NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
-                    ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
-        consumer.accept(snapshotComputer());
-    }
-
-    private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
-            @NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
-            throws ExceptionType {
-        return function.apply(snapshotComputer());
-    }
-
     boolean isHistoricalPackageUsageAvailable() {
         return mPackageUsage.isHistoricalPackageUsageAvailable();
     }
@@ -8339,16 +8173,6 @@
         }
     }
 
-    private void applyMimeGroupChanges(String packageName, String mimeGroup) {
-        if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
-            Binder.withCleanCallingIdentity(() ->
-                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
-                            UserHandle.USER_ALL));
-        }
-
-        mPmInternal.writeSettings(false);
-    }
-
     @Override
     public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
         enforceOwnerRights(packageName, Binder.getCallingUid());
@@ -8368,7 +8192,13 @@
         commitPackageStateMutation(null, packageName, packageStateWrite -> {
             packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
         });
-        applyMimeGroupChanges(packageName, mimeGroup);
+        if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
+            Binder.withCleanCallingIdentity(() ->
+                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+                            UserHandle.USER_ALL));
+        }
+
+        scheduleWriteSettings();
     }
 
     @Override
@@ -8427,7 +8257,7 @@
      */
     void writeSettingsLPrTEMP() {
         mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
-        mSettings.writeLPr();
+        mSettings.writeLPr(mLiveComputer);
     }
 
     @Override
@@ -8600,8 +8430,8 @@
     @Override
     public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
             String featureId, int userId) throws RemoteException {
-        return mResolveIntentHelper.getLaunchIntentSenderForPackage(packageName, callingPackage,
-                featureId, userId);
+        return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
+                packageName, callingPackage, featureId, userId);
     }
 
     @Override
@@ -8926,7 +8756,6 @@
     }
 
     void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
-        executeWithConsistentComputer(computer ->
-                mInstantAppRegistry.onPackageInstalled(computer, packageName, newUsers));
+        mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 05bb01e..a02237f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -34,6 +34,7 @@
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.resolution.ComponentResolver;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import java.util.List;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 2f56bfe..a9471cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -93,6 +93,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import dalvik.system.VMRuntime;
@@ -1064,7 +1065,7 @@
 
     // Static to give access to ComputeEngine
     public static void applyEnforceIntentFilterMatching(
-            PlatformCompat compat, ComponentResolver resolver,
+            PlatformCompat compat, ComponentResolverApi resolver,
             List<ResolveInfo> resolveInfos, boolean isReceiver,
             Intent intent, String resolvedType, int filterCallingUid) {
         // Do not enforce filter matching when the caller is system or root.
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 6b57deb..2016fc3 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -24,7 +24,6 @@
 import android.content.Intent;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
@@ -110,7 +109,7 @@
                 verifyAPK(session, callback);
             } catch (PackageManagerException e) {
                 String errorMessage = PackageManager.installStatusToString(e.error, e.getMessage());
-                session.setSessionFailed(SessionInfo.SESSION_VERIFICATION_FAILED, errorMessage);
+                session.setSessionFailed(e.error, errorMessage);
                 callback.onResult(e.error, e.getMessage());
             }
         });
@@ -137,7 +136,7 @@
                 }
                 if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
                     String errorMessage = PackageManager.installStatusToString(returnCode, msg);
-                    session.setSessionFailed(SessionInfo.SESSION_VERIFICATION_FAILED, errorMessage);
+                    session.setSessionFailed(returnCode, errorMessage);
                     callback.onResult(returnCode, msg);
                 } else {
                     session.setSessionReady();
@@ -220,7 +219,7 @@
     }
 
     private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
-            @SessionInfo.SessionErrorCode int errorCode, String errorMessage) {
+            int errorCode, String errorMessage) {
         if (!ensureActiveApexSessionIsAborted(session)) {
             Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
             // Safe to ignore active apex session abortion failure since session will be marked
@@ -312,7 +311,7 @@
             // Failed to get hold of StorageManager
             Slog.e(TAG, "Failed to get hold of StorageManager", e);
             throw new PackageManagerException(
-                    SessionInfo.SESSION_UNKNOWN_ERROR,
+                    PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                     "Failed to get hold of StorageManager");
         }
         // Proactively mark session as ready before calling apexd. Although this call order
@@ -350,7 +349,7 @@
         final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
                 input.reset(), apexPath, minSignatureScheme);
         if (newResult.isError()) {
-            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                     "Failed to parse APEX package " + apexPath + " : "
                             + newResult.getException(), newResult.getException());
         }
@@ -369,7 +368,7 @@
                 input.reset(), existingApexPkg.applicationInfo.sourceDir,
                 SigningDetails.SignatureSchemeVersion.JAR);
         if (existingResult.isError()) {
-            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                     "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
                             + " : " + existingResult.getException(), existingResult.getException());
         }
@@ -383,7 +382,7 @@
             return;
         }
 
-        throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+        throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                 "APK-container signature of APEX package " + packageName + " with version "
                         + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
                         + " compatible with the one currently installed on device");
@@ -426,11 +425,12 @@
                 packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
                 if (packageInfo == null) {
                     throw new PackageManagerException(
-                            SessionInfo.SESSION_VERIFICATION_FAILED,
+                            PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                             "Unable to generate package info: " + apexInfo.modulePath);
                 }
             } catch (PackageManagerException e) {
-                throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+                throw new PackageManagerException(
+                        PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                         "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
             }
             result.add(packageInfo);
@@ -452,7 +452,7 @@
             }
         }
         throw new PackageManagerException(
-                SessionInfo.SESSION_VERIFICATION_FAILED,
+                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                 "Could not find rollback id for commit session: " + sessionId);
     }
 
@@ -560,7 +560,7 @@
         try {
             checkActiveSessions(InstallLocationUtils.getStorageManager().supportsCheckpoint());
         } catch (RemoteException e) {
-            throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED,
+            throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                     "Can't query fs-checkpoint status : " + e);
         }
     }
@@ -576,7 +576,7 @@
         }
         if (!supportsCheckpoint && activeSessions > 1) {
             throw new PackageManagerException(
-                    SessionInfo.SESSION_VERIFICATION_FAILED,
+                    PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                     "Cannot stage multiple sessions without checkpoint support");
         }
     }
@@ -607,13 +607,13 @@
                     // will be deleted.
                 }
                 stagedSession.setSessionFailed(
-                        SessionInfo.SESSION_CONFLICT,
+                        PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                         "Session was failed by rollback session: " + session.sessionId());
                 Slog.i(TAG, "Session " + stagedSession.sessionId() + " is marked failed due to "
                         + "rollback session: " + session.sessionId());
             } else if (!isRollback(session) && isRollback(stagedSession)) {
                 throw new PackageManagerException(
-                        SessionInfo.SESSION_CONFLICT,
+                        PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                         "Session was failed by rollback session: " + stagedSession.sessionId());
 
             }
@@ -636,7 +636,7 @@
         final String packageName = child.getPackageName();
         if (packageName == null) {
             throw new PackageManagerException(
-                    SessionInfo.SESSION_VERIFICATION_FAILED,
+                    PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                     "Cannot stage session " + child.sessionId() + " with package name null");
         }
         for (StagingManager.StagedSession stagedSession : mStagedSessions) {
@@ -648,14 +648,14 @@
                 if (stagedSession.getCommittedMillis() < parent.getCommittedMillis()) {
                     // Fail the session committed later when there are overlapping packages
                     throw new PackageManagerException(
-                            SessionInfo.SESSION_VERIFICATION_FAILED,
+                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                             "Package: " + packageName + " in session: "
                                     + child.sessionId()
                                     + " has been staged already by session: "
                                     + stagedSession.sessionId());
                 } else {
                     stagedSession.setSessionFailed(
-                            SessionInfo.SESSION_VERIFICATION_FAILED,
+                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                             "Package: " + packageName + " in session: "
                                     + stagedSession.sessionId()
                                     + " has been staged already by session: "
diff --git a/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java b/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java
index 4e9a06a..6e2f756 100644
--- a/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java
+++ b/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java
@@ -16,12 +16,17 @@
 
 package com.android.server.pm;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Set of pending broadcasts for aggregating enable/disable of components.
@@ -29,65 +34,111 @@
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public final class PendingPackageBroadcasts {
 
+    private final Object mLock = new PackageManagerTracedLock();
+
     // for each user id, a map of <package name -> components within that package>
+    @GuardedBy("mLock")
     final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
 
     public PendingPackageBroadcasts() {
         mUidMap = new SparseArray<>(2);
     }
 
-    public ArrayList<String> get(int userId, String packageName) {
-        ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
-        return packages.get(packageName);
+    public boolean hasPackage(@UserIdInt int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            final ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
+            return packages != null && packages.containsKey(packageName);
+        }
     }
 
     public void put(int userId, String packageName, ArrayList<String> components) {
-        ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
-        packages.put(packageName, components);
+        synchronized (mLock) {
+            ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+            packages.put(packageName, components);
+        }
+    }
+
+    public void addComponent(@UserIdInt int userId, @NonNull String packageName,
+            @NonNull String componentClassName) {
+        synchronized (mLock) {
+            ArrayList<String> components = getOrAllocate(userId, packageName);
+            if (!components.contains(componentClassName)) {
+                components.add(componentClassName);
+            }
+        }
+    }
+
+    public void addComponents(@UserIdInt int userId, @NonNull String packageName,
+            @NonNull List<String> componentClassNames) {
+        synchronized (mLock) {
+            ArrayList<String> components = getOrAllocate(userId, packageName);
+            for (int index = 0; index < componentClassNames.size(); index++) {
+                String componentClassName = componentClassNames.get(index);
+                if (!components.contains(componentClassName)) {
+                    components.add(componentClassName);
+                }
+            }
+        }
     }
 
     public void remove(int userId, String packageName) {
-        ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
-        if (packages != null) {
-            packages.remove(packageName);
+        synchronized (mLock) {
+            ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
+            if (packages != null) {
+                packages.remove(packageName);
+            }
         }
     }
 
     public void remove(int userId) {
-        mUidMap.remove(userId);
-    }
-
-    public int userIdCount() {
-        return mUidMap.size();
-    }
-
-    public int userIdAt(int n) {
-        return mUidMap.keyAt(n);
-    }
-
-    public ArrayMap<String, ArrayList<String>> packagesForUserId(int userId) {
-        return mUidMap.get(userId);
-    }
-
-    public int size() {
-        // total number of pending broadcast entries across all userIds
-        int num = 0;
-        for (int i = 0; i < mUidMap.size(); i++) {
-            num += mUidMap.valueAt(i).size();
+        synchronized (mLock) {
+            mUidMap.remove(userId);
         }
-        return num;
+    }
+
+    @Nullable
+    public SparseArray<ArrayMap<String, ArrayList<String>>> copiedMap() {
+        synchronized (mLock) {
+            SparseArray<ArrayMap<String, ArrayList<String>>> copy = new SparseArray<>();
+            for (int userIdIndex = 0; userIdIndex < mUidMap.size(); userIdIndex++) {
+                final ArrayMap<String, ArrayList<String>> packages = mUidMap.valueAt(userIdIndex);
+                ArrayMap<String, ArrayList<String>> packagesCopy = new ArrayMap<>();
+                for (int packagesIndex = 0; packagesIndex < packages.size(); packagesIndex++) {
+                    packagesCopy.put(packages.keyAt(packagesIndex),
+                            new ArrayList<>(packages.valueAt(packagesIndex)));
+                }
+                copy.put(mUidMap.keyAt(userIdIndex), packagesCopy);
+            }
+            return copy;
+        }
     }
 
     public void clear() {
-        mUidMap.clear();
+        synchronized (mLock) {
+            mUidMap.clear();
+        }
     }
 
     private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
-        ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
-        if (map == null) {
-            map = new ArrayMap<>();
-            mUidMap.put(userId, map);
+        synchronized (mLock) {
+            ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
+            if (map == null) {
+                map = new ArrayMap<>();
+                mUidMap.put(userId, map);
+            }
+            return map;
         }
-        return map;
+    }
+
+    private ArrayList<String> getOrAllocate(int userId, @NonNull String packageName) {
+        synchronized (mLock) {
+            ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
+            if (map == null) {
+                map = new ArrayMap<>();
+                mUidMap.put(userId, map);
+            }
+
+            return map.computeIfAbsent(packageName, k -> new ArrayList<>());
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index bb82e6a..8c49baf 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -140,8 +140,8 @@
             return false;
         }
         final Intent intent = mPm.getHomeIntent();
-        final List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesInternal(intent, null,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
+        final List<ResolveInfo> resolveInfos = mPm.snapshotComputer().queryIntentActivitiesInternal(
+                intent, null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
         final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
                 intent, null, 0, resolveInfos, true, false, false, userId);
         final String packageName = preferredResolveInfo != null
@@ -209,7 +209,8 @@
             if (removeExisting && existing != null) {
                 Settings.removeFilters(pir, filter, existing);
             }
-            pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
+            pir.addFilter(mPm.snapshotComputer(),
+                    new PreferredActivity(filter, match, set, activity, always));
             mPm.scheduleWritePackageRestrictions(userId);
         }
         if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
@@ -394,6 +395,7 @@
         }
         synchronized (mPm.mLock) {
             mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
+                    mPm.snapshotComputer(),
                     new PersistentPreferredActivity(filter, activity, true));
             mPm.scheduleWritePackageRestrictions(userId);
         }
@@ -672,8 +674,8 @@
                 0, userId, callingUid, false /*includeInstantApps*/,
                 mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
                         0));
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         synchronized (mPm.mLock) {
             return mPm.findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
                     userId);
@@ -699,8 +701,8 @@
             filter.dump(new PrintStreamPrinter(System.out), "    ");
         }
         intent.setComponent(null);
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         // Find any earlier preferred or last chosen entries and nuke them
         findPreferredActivityNotLocked(
                 intent, resolvedType, flags, query, false, true, false, userId);
@@ -715,8 +717,8 @@
         }
         final int userId = UserHandle.getCallingUserId();
         if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         return findPreferredActivityNotLocked(
                 intent, resolvedType, flags, query, false, false, false, userId);
     }
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 2aff90f..25356a4 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -27,6 +27,8 @@
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -50,23 +52,52 @@
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Supplier;
 
 final class ResolveIntentHelper {
-    private final PackageManagerService mPm;
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final PlatformCompat mPlatformCompat;
+    @NonNull
+    private final UserManagerService mUserManager;
+    @NonNull
     private final PreferredActivityHelper mPreferredActivityHelper;
+    @NonNull
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
+    @NonNull
+    private final UserNeedsBadgingCache mUserNeedsBadging;
+    @NonNull
+    private final Supplier<ResolveInfo> mResolveInfoSupplier;
+    @NonNull
+    private final Supplier<ActivityInfo> mInstantAppInstallerActivitySupplier;
 
-    // TODO(b/198166813): remove PMS dependency
-    ResolveIntentHelper(PackageManagerService pm, PreferredActivityHelper preferredActivityHelper) {
-        mPm = pm;
+    ResolveIntentHelper(@NonNull Context context,
+            @NonNull PreferredActivityHelper preferredActivityHelper,
+            @NonNull PlatformCompat platformCompat, @NonNull UserManagerService userManager,
+            @NonNull DomainVerificationManagerInternal domainVerificationManager,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache,
+            @NonNull Supplier<ResolveInfo> resolveInfoSupplier,
+            @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier) {
+        mContext = context;
         mPreferredActivityHelper = preferredActivityHelper;
+        mPlatformCompat = platformCompat;
+        mUserManager = userManager;
+        mDomainVerificationManager = domainVerificationManager;
+        mUserNeedsBadging = userNeedsBadgingCache;
+        mResolveInfoSupplier = resolveInfoSupplier;
+        mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
     }
 
     /**
@@ -74,35 +105,33 @@
      * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
      * since we need to allow the system to start any installed application.
      */
-    public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+    public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
             boolean resolveForStart, int filterCallingUid) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
-            if (!mPm.mUserManager.exists(userId)) return null;
+            if (!mUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
-            flags = mPm.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
-                    mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+            flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+                    computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
                             resolvedType, flags));
-            mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+            computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                     false /*checkShell*/, "resolve intent");
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
-            final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                    flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
-                    true /*allowDynamicSplits*/);
+            final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
+                    resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
+                    resolveForStart, true /*allowDynamicSplits*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             final boolean queryMayBeFiltered =
                     UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
                             && !resolveForStart;
 
-            final ResolveInfo bestChoice =
-                    chooseBestActivity(
-                            intent, resolvedType, flags, privateResolveFlags, query, userId,
-                            queryMayBeFiltered);
+            final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags,
+                    privateResolveFlags, query, userId, queryMayBeFiltered);
             final boolean nonBrowserOnly =
                     (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
             if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -114,7 +143,7 @@
         }
     }
 
-    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
+    private ResolveInfo chooseBestActivity(Computer computer, Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
@@ -156,9 +185,10 @@
                     // If we have an ephemeral app, use it
                     if (ri.activityInfo.applicationInfo.isInstantApp()) {
                         final String packageName = ri.activityInfo.packageName;
-                        final PackageStateInternal ps = mPm.getPackageStateInternal(packageName);
+                        final PackageStateInternal ps =
+                                computer.getPackageStateInternal(packageName);
                         if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
-                                mPm.mDomainVerificationManager, ps, intent, flags, userId)) {
+                                mDomainVerificationManager, ps, intent, flags, userId)) {
                             return ri;
                         }
                     }
@@ -167,7 +197,7 @@
                         & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
                     return null;
                 }
-                ri = new ResolveInfo(mPm.getResolveInfo());
+                ri = new ResolveInfo(mResolveInfoSupplier.get());
                 // if all resolve options are browsers, mark the resolver's info as if it were
                 // also a browser.
                 ri.handleAllWebDataURI = browserCount == n;
@@ -184,7 +214,7 @@
                 if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
                     final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
                     ri.resolvePackageName = intentPackage;
-                    if (mPm.userNeedsBadging(userId)) {
+                    if (mUserNeedsBadging.get(userId)) {
                         ri.noResourceId = true;
                     } else {
                         ri.icon = appi.icon;
@@ -225,13 +255,14 @@
         return true;
     }
 
-    public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
-            String featureId, int userId) throws RemoteException {
+    public IntentSender getLaunchIntentSenderForPackage(@NonNull Computer computer,
+            String packageName, String callingPackage, String featureId, int userId)
+            throws RemoteException {
         Objects.requireNonNull(packageName);
         final int callingUid = Binder.getCallingUid();
-        mPm.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+        computer.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                 false /* checkShell */, "get launch intent sender for package");
-        final int packageUid = mPm.getPackageUid(callingPackage, 0 /* flags */, userId);
+        final int packageUid = computer.getPackageUid(callingPackage, 0 /* flags */, userId);
         if (!UserHandle.isSameApp(callingUid, packageUid)) {
             throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
                     + callingUid + " does not own package: " + callingPackage);
@@ -242,17 +273,17 @@
         final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_INFO);
         intentToResolve.setPackage(packageName);
-        String resolvedType = intentToResolve.resolveTypeIfNeeded(
-                mPm.mContext.getContentResolver());
-        List<ResolveInfo> ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        String resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+        List<ResolveInfo> ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
                 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
                 true /* resolveForStart */, false /* allowDynamicSplits */);
         if (ris == null || ris.size() <= 0) {
             intentToResolve.removeCategory(Intent.CATEGORY_INFO);
             intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
             intentToResolve.setPackage(packageName);
-            resolvedType = intentToResolve.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
-            ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+            resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+            ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
                     0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
                     true /* resolveForStart */, false /* allowDynamicSplits */);
         }
@@ -277,17 +308,17 @@
 
     // In this method, we have to know the actual calling UID, but in some cases Binder's
     // call identity is removed, so the UID has to be passed in explicitly.
-    public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
+    public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int filterCallingUid) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
-        mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
+        computer.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
                 false /*checkShell*/, "query intent receivers");
-        final String instantAppPkgName = mPm.getInstantAppPackageName(filterCallingUid);
-        flags = mPm.updateFlagsForResolve(
-                flags, userId, filterCallingUid, false /*includeInstantApps*/,
-                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
+        final String instantAppPkgName = computer.getInstantAppPackageName(filterCallingUid);
+        flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid,
+                false /*includeInstantApps*/,
+                computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                        resolvedType, flags));
         Intent originalIntent = null;
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -297,9 +328,10 @@
                 comp = intent.getComponent();
             }
         }
+        final ComponentResolverApi componentResolver = computer.getComponentResolver();
         List<ResolveInfo> list = Collections.emptyList();
         if (comp != null) {
-            final ActivityInfo ai = mPm.getReceiverInfo(comp, flags, userId);
+            final ActivityInfo ai = computer.getReceiverInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
                 // used when either 1) the calling package is normal and the activity is within
@@ -335,28 +367,25 @@
                     list = new ArrayList<>(1);
                     list.add(ri);
                     PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                            mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
-                            list, true, intent, resolvedType, filterCallingUid);
+                            mPlatformCompat, componentResolver, list, true, intent,
+                            resolvedType, filterCallingUid);
                 }
             }
         } else {
-            // reader
-            synchronized (mPm.mLock) {
-                String pkgName = intent.getPackage();
-                if (pkgName == null) {
-                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
-                            intent, resolvedType, flags, userId);
-                    if (result != null) {
-                        list = result;
-                    }
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                final List<ResolveInfo> result = componentResolver
+                        .queryReceivers(computer, intent, resolvedType, flags, userId);
+                if (result != null) {
+                    list = result;
                 }
-                final AndroidPackage pkg = mPm.mPackages.get(pkgName);
-                if (pkg != null) {
-                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
-                            intent, resolvedType, flags, pkg.getReceivers(), userId);
-                    if (result != null) {
-                        list = result;
-                    }
+            }
+            final AndroidPackage pkg = computer.getPackage(pkgName);
+            if (pkg != null) {
+                final List<ResolveInfo> result = componentResolver.queryReceivers(computer,
+                        intent, resolvedType, flags, pkg.getReceivers(), userId);
+                if (result != null) {
+                    list = result;
                 }
             }
         }
@@ -364,21 +393,22 @@
         if (originalIntent != null) {
             // We also have to ensure all components match the original intent
             PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                    mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
+                    mPlatformCompat, componentResolver,
                     list, true, originalIntent, resolvedType, filterCallingUid);
         }
 
-        return mPm.applyPostResolutionFilter(
-                list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
+        return computer.applyPostResolutionFilter(list, instantAppPkgName, false, filterCallingUid,
+                false, userId, intent);
     }
 
 
-    public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
-        if (!mPm.mUserManager.exists(userId)) return null;
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+    public ResolveInfo resolveServiceInternal(@NonNull Computer computer, Intent intent,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
+            int callingUid) {
+        if (!mUserManager.exists(userId)) return null;
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
-        List<ResolveInfo> query = mPm.queryIntentServicesInternal(
+        List<ResolveInfo> query = computer.queryIntentServicesInternal(
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
         if (query != null) {
             if (query.size() >= 1) {
@@ -391,12 +421,12 @@
     }
 
     public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
-            int userId) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+            @NonNull Computer computer, Intent intent, String resolvedType,
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        final String instantAppPkgName = mPm.getInstantAppPackageName(callingUid);
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+        final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -407,7 +437,7 @@
         }
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<>(1);
-            final ProviderInfo pi = mPm.getProviderInfo(comp, flags, userId);
+            final ProviderInfo pi = computer.getProviderInfo(comp, flags, userId);
             if (pi != null) {
                 // When specifying an explicit component, we prevent the provider from being
                 // used when either 1) the provider is in an instant application and the
@@ -432,8 +462,8 @@
                                 || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                 && isTargetHiddenFromInstantApp));
                 final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
-                        && mPm.shouldFilterApplication(
-                        mPm.getPackageStateInternal(pi.applicationInfo.packageName,
+                        && computer.shouldFilterApplication(
+                        computer.getPackageStateInternal(pi.applicationInfo.packageName,
                                 Process.SYSTEM_UID), callingUid, userId);
                 if (!blockResolution && !blockNormalResolution) {
                     final ResolveInfo ri = new ResolveInfo();
@@ -444,46 +474,40 @@
             return list;
         }
 
-        // reader
-        synchronized (mPm.mLock) {
-            String pkgName = intent.getPackage();
-            if (pkgName == null) {
-                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
-                        resolvedType, flags, userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
+        final ComponentResolverApi componentResolver = computer.getComponentResolver();
+        String pkgName = intent.getPackage();
+        if (pkgName == null) {
+            final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+                    intent, resolvedType, flags, userId);
+            if (resolveInfos == null) {
+                return Collections.emptyList();
             }
-            final AndroidPackage pkg = mPm.mPackages.get(pkgName);
-            if (pkg != null) {
-                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
-                        resolvedType, flags,
-                        pkg.getProviders(), userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
-            }
-            return Collections.emptyList();
+            return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+                    instantAppPkgName, userId, callingUid);
         }
+        final AndroidPackage pkg = computer.getPackage(pkgName);
+        if (pkg != null) {
+            final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+                    intent, resolvedType, flags, pkg.getProviders(), userId);
+            if (resolveInfos == null) {
+                return Collections.emptyList();
+            }
+            return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+                    instantAppPkgName, userId, callingUid);
+        }
+        return Collections.emptyList();
     }
 
-    private List<ResolveInfo> applyPostContentProviderResolutionFilter(
+    private List<ResolveInfo> applyPostContentProviderResolutionFilter(@NonNull Computer computer,
             List<ResolveInfo> resolveInfos, String instantAppPkgName,
             @UserIdInt int userId, int callingUid) {
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             final ResolveInfo info = resolveInfos.get(i);
 
             if (instantAppPkgName == null) {
-                SettingBase callingSetting =
-                        mPm.mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
-                PackageStateInternal resolvedSetting =
-                        mPm.getPackageStateInternal(info.providerInfo.packageName, 0);
-                if (!mPm.mAppsFilter.shouldFilterApplication(
-                        callingUid, callingSetting, resolvedSetting, userId)) {
+                PackageStateInternal resolvedSetting = computer.getPackageStateInternal(
+                        info.providerInfo.packageName, 0);
+                if (!computer.shouldFilterApplication(resolvedSetting, callingUid, userId)) {
                     continue;
                 }
             }
@@ -494,7 +518,7 @@
                 if (info.providerInfo.splitName != null
                         && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
                         info.providerInfo.splitName)) {
-                    if (mPm.mInstantAppInstallerActivity == null) {
+                    if (mInstantAppInstallerActivitySupplier.get() == null) {
                         if (DEBUG_INSTANT) {
                             Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
                         }
@@ -507,7 +531,7 @@
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
                     final ResolveInfo installerInfo = new ResolveInfo(
-                            mPm.getInstantAppInstallerInfo());
+                            computer.getInstantAppInstallerInfo());
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                             null /*failureActivity*/,
                             info.providerInfo.packageName,
@@ -531,20 +555,21 @@
         return resolveInfos;
     }
 
-    public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
-            Intent[] specifics, String[] specificTypes, Intent intent,
+    public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(Computer computer,
+            ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
-                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
-        mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid,
+                false /*includeInstantApps*/,
+                computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                        resolvedType, flags));
+        computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                 false /*checkShell*/, "query intent activity options");
         final String resultsAction = intent.getAction();
 
-        final List<ResolveInfo> results = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags | PackageManager.GET_RESOLVED_FILTER, userId);
+        final List<ResolveInfo> results = computer.queryIntentActivitiesInternal(intent,
+                resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId);
 
         if (DEBUG_INTENT_MATCHING) {
             Log.v(TAG, "Query " + intent + ": " + results);
@@ -584,21 +609,20 @@
 
                 ComponentName comp = sintent.getComponent();
                 if (comp == null) {
-                    ri = mPm.resolveIntent(
-                            sintent,
-                            specificTypes != null ? specificTypes[i] : null,
-                            flags, userId);
+                    ri = resolveIntentInternal(computer, sintent,
+                            specificTypes != null ? specificTypes[i] : null, flags,
+                            0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
                     if (ri == null) {
                         continue;
                     }
-                    if (ri == mPm.getResolveInfo()) {
+                    if (ri == mResolveInfoSupplier.get()) {
                         // ACK!  Must do something better with this.
                     }
                     ai = ri.activityInfo;
                     comp = new ComponentName(ai.applicationInfo.packageName,
                             ai.name);
                 } else {
-                    ai = mPm.getActivityInfo(comp, flags, userId);
+                    ai = computer.getActivityInfo(comp, flags, userId);
                     if (ai == null) {
                         continue;
                     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c19e758..394c8fb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
 
@@ -119,6 +120,7 @@
 import com.android.server.pm.pkg.component.ParsedPermission;
 import com.android.server.pm.pkg.component.ParsedProcess;
 import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.resolution.ComponentResolver;
 import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationPersistence;
@@ -136,6 +138,8 @@
 import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -1117,6 +1121,9 @@
                         "Updating application package " + pkgName + " failed");
             }
             pkgSetting.setSharedUserAppId(sharedUser.mAppId);
+        } else {
+            // migrating off shared user
+            pkgSetting.setSharedUserAppId(INVALID_UID);
         }
 
         if (!pkgSetting.getPath().equals(codePath)) {
@@ -1545,7 +1552,7 @@
                 if (pa.mPref.getParseError() == null) {
                     final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
                     if (resolver.shouldAddPreferredActivity(pa)) {
-                        resolver.addFilter(pa);
+                        resolver.addFilter(null, pa);
                     }
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -1573,7 +1580,7 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_ITEM)) {
                 PersistentPreferredActivity ppa = new PersistentPreferredActivity(parser);
-                editPersistentPreferredActivitiesLPw(userId).addFilter(ppa);
+                editPersistentPreferredActivitiesLPw(userId).addFilter(null, ppa);
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <" + TAG_PERSISTENT_PREFERRED_ACTIVITIES + ">: "
@@ -1595,7 +1602,7 @@
             final String tagName = parser.getName();
             if (tagName.equals(TAG_ITEM)) {
                 CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser);
-                editCrossProfileIntentResolverLPw(userId).addFilter(cpif);
+                editCrossProfileIntentResolverLPw(userId).addFilter(null, cpif);
             } else {
                 String msg = "Unknown element under " +  TAG_CROSS_PROFILE_INTENT_FILTERS + ": " +
                         tagName;
@@ -1862,8 +1869,7 @@
                                 .build();
                     }
                     if (suspended && suspendParamsMap == null) {
-                        final SuspendParams suspendParams =
-                                SuspendParams.getInstanceOrNull(
+                        final SuspendParams suspendParams = new SuspendParams(
                                         oldSuspendDialogInfo,
                                         suspendedAppExtras,
                                         suspendedLauncherExtras);
@@ -2426,7 +2432,7 @@
         }
     }
 
-    void writeLPr() {
+    void writeLPr(@NonNull Computer computer) {
         //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024);
 
         final long startTime = SystemClock.uptimeMillis();
@@ -2518,8 +2524,8 @@
                 }
             }
 
-            mDomainVerificationManager.writeSettings(serializer, false /* includeSignatures */,
-                    UserHandle.USER_ALL);
+            mDomainVerificationManager.writeSettings(computer, serializer,
+                    false /* includeSignatures */, UserHandle.USER_ALL);
 
             mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
 
@@ -2961,7 +2967,7 @@
         }
     }
 
-    boolean readLPw(@NonNull List<UserInfo> users) {
+    boolean readLPw(@NonNull Computer computer, @NonNull List<UserInfo> users) {
         FileInputStream str = null;
         if (mBackupSettingsFilename.exists()) {
             try {
@@ -3105,7 +3111,7 @@
                     ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION);
                     ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
                 } else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) {
-                    mDomainVerificationManager.readSettings(parser);
+                    mDomainVerificationManager.readSettings(computer, parser);
                 } else if (tagName.equals(
                         DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
                     mDomainVerificationManager.readLegacySettings(parser);
@@ -3511,7 +3517,7 @@
                 removeFilters(pir, filter, existing);
             }
             PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true);
-            pir.addFilter(pa);
+            pir.addFilter(null, pa);
         } else if (haveNonSys == null) {
             StringBuilder sb = new StringBuilder();
             sb.append("No component ");
@@ -4281,11 +4287,11 @@
         return Process.FIRST_APPLICATION_UID + size;
     }
 
-    public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
+    public VerifierDeviceIdentity getVerifierDeviceIdentityLPw(@NonNull Computer computer) {
         if (mVerifierDeviceIdentity == null) {
             mVerifierDeviceIdentity = VerifierDeviceIdentity.generate();
 
-            writeLPr();
+            writeLPr(computer);
         }
 
         return mVerifierDeviceIdentity;
@@ -4514,6 +4520,7 @@
         pw.decreaseIndent();
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
             ArraySet<String> permissionNames, PackageSetting ps,
             LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date,
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 0638d5e..3fe0790 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -321,7 +321,8 @@
      *                       on the device.
      * @return {@code true} if the available storage space is reached.
      */
-    boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
+    boolean pruneUnusedStaticSharedLibraries(@NonNull Computer computer, long neededSpace,
+            long maxCachePeriod)
             throws IOException {
         final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
@@ -332,38 +333,36 @@
         // 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.
-        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 =
-                        sharedLibraries.valueAt(i);
-                if (versionedLib == null) {
+        final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+                sharedLibraries = computer.getSharedLibraries();
+        final int libCount = sharedLibraries.size();
+        for (int i = 0; i < libCount; i++) {
+            final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+                    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 PackageStateInternal ps = getLibraryPackage(computer, libInfo);
+                if (ps == null) {
                     continue;
                 }
-                final int versionCount = versionedLib.size();
-                for (int j = 0; j < versionCount; j++) {
-                    SharedLibraryInfo libInfo = versionedLib.valueAt(j);
-                    final PackageStateInternal ps = getLibraryPackage(computer, libInfo);
-                    if (ps == null) {
-                        continue;
-                    }
-                    // Skip unused libs cached less than the min period to prevent pruning a lib
-                    // needed by a subsequently installed package.
-                    if (now - ps.getLastUpdateTime() < maxCachePeriod) {
-                        continue;
-                    }
-
-                    if (ps.getPkg().isSystem()) {
-                        continue;
-                    }
-
-                    packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
-                            libInfo.getDeclaringPackage().getLongVersionCode()));
+                // Skip unused libs cached less than the min period to prevent pruning a lib
+                // needed by a subsequently installed package.
+                if (now - ps.getLastUpdateTime() < maxCachePeriod) {
+                    continue;
                 }
+
+                if (ps.getPkg().isSystem()) {
+                    continue;
+                }
+
+                packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
+                        libInfo.getDeclaringPackage().getLongVersionCode()));
             }
-        });
+        }
 
         final int packageCount = packagesToDelete.size();
         for (int i = 0; i < packageCount; i++) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 8921fee..2215acb 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -341,7 +341,7 @@
 
     public void ensureAllShortcutsVisibleToLauncher(@NonNull List<ShortcutInfo> shortcuts) {
         for (ShortcutInfo shortcut : shortcuts) {
-            if (!shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+            if (shortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
                 throw new IllegalArgumentException("Shortcut ID=" + shortcut.getId()
                         + " is hidden from launcher and may not be manipulated via APIs");
             }
@@ -402,7 +402,7 @@
                     & (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
         }
 
-        if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+        if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
             if (isAppSearchEnabled()) {
                 synchronized (mLock) {
                     mTransientShortcuts.put(newShortcut.getId(), newShortcut);
@@ -480,7 +480,7 @@
                     & (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
         }
 
-        if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+        if (newShortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER)) {
             if (isAppSearchEnabled()) {
                 synchronized (mLock) {
                     mTransientShortcuts.put(newShortcut.getId(), newShortcut);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 1cf2dc5..6e29c30 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2227,7 +2227,7 @@
         Objects.requireNonNull(shortcut);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
         Preconditions.checkArgument(
-                shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER),
+                !shortcut.isExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER),
                 "Shortcut excluded from launcher cannot be pinned");
         ret.complete(String.valueOf(requestPinItem(
                 packageName, userId, shortcut, null, null, resultIntent)));
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 52a7bed..43dde5c 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,8 +28,6 @@
 import android.content.pm.ApexStagedEvent;
 import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.StagedApexInfo;
@@ -124,7 +122,7 @@
         boolean containsApkSession();
         boolean containsApexSession();
         void setSessionReady();
-        void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage);
+        void setSessionFailed(int errorCode, String errorMessage);
         void setSessionApplied();
         CompletableFuture<Void> installSession();
         boolean hasParentSessionId();
@@ -279,7 +277,7 @@
             String packageName = apexSession.getPackageName();
             String errorMsg = mApexManager.getApkInApexInstallError(packageName);
             if (errorMsg != null) {
-                throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED,
+                throw new PackageManagerException(PackageManager.INSTALL_ACTIVATION_FAILED,
                         "Failed to install apk-in-apex of " + packageName + " : " + errorMsg);
             }
         }
@@ -392,7 +390,7 @@
                 revertMsg += " Reason for revert: " + reasonForRevert;
             }
             Slog.d(TAG, revertMsg);
-            session.setSessionFailed(SessionInfo.SESSION_UNKNOWN_ERROR, revertMsg);
+            session.setSessionFailed(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, revertMsg);
             return;
         }
 
@@ -477,7 +475,7 @@
             for (String apkInApex : mApexManager.getApksInApex(packageName)) {
                 if (!apkNames.add(apkInApex)) {
                     throw new PackageManagerException(
-                            SessionInfo.SESSION_ACTIVATION_FAILED,
+                            PackageManager.INSTALL_ACTIVATION_FAILED,
                             "Package: " + packageName + " in session: "
                                     + apexSession.sessionId() + " has duplicate apk-in-apex: "
                                     + apkInApex, null);
@@ -495,9 +493,7 @@
             // Should be impossible
             throw new RuntimeException(e);
         } catch (ExecutionException ee) {
-            PackageManagerException e = (PackageManagerException) ee.getCause();
-            final String errorMsg = PackageManager.installStatusToString(e.error, e.getMessage());
-            throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg);
+            throw (PackageManagerException) ee.getCause();
         }
     }
 
@@ -651,7 +647,7 @@
             // is upgrading. Fail all the sessions and exit early.
             for (int i = 0; i < sessions.size(); i++) {
                 StagedSession session = sessions.get(i);
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
                         "Build fingerprint has changed");
             }
             return;
@@ -691,7 +687,7 @@
             final ApexSessionInfo apexSession = apexSessions.get(session.sessionId());
             if (apexSession == null || apexSession.isUnknown) {
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "apexd did "
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED, "apexd did "
                         + "not know anything about a staged session supposed to be activated");
                 continue;
             } else if (isApexSessionFailed(apexSession)) {
@@ -707,7 +703,7 @@
                     errorMsg += " Error: " + apexSession.errorMessage;
                 }
                 Slog.d(TAG, errorMsg);
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg);
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED, errorMsg);
                 continue;
             } else if (apexSession.isActivated || apexSession.isSuccess) {
                 hasAppliedApexSession = true;
@@ -716,13 +712,13 @@
                 // Apexd did not apply the session for some unknown reason. There is no guarantee
                 // that apexd will install it next time. Safer to proactively mark it as failed.
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
                         "Staged session " + session.sessionId() + " at boot didn't activate nor "
                         + "fail. Marking it as failed anyway.");
             } else {
                 Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state");
                 hasFailedApexSession = true;
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
                         "Impossible state");
             }
         }
@@ -742,7 +738,7 @@
                     // Session has been already failed in the loop above.
                     continue;
                 }
-                session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED,
+                session.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
                         "Another apex session failed");
             }
             return;
@@ -758,7 +754,7 @@
             } catch (Exception e) {
                 Slog.e(TAG, "Staged install failed due to unhandled exception", e);
                 onInstallationFailure(session, new PackageManagerException(
-                        SessionInfo.SESSION_ACTIVATION_FAILED,
+                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                         "Staged install failed due to unhandled exception: " + e),
                         supportsCheckpoint, needsCheckpoint);
             }
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 85d1367..3ef5599 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -27,6 +27,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Intent;
@@ -99,10 +100,10 @@
      * @return The names of failed packages.
      */
     @Nullable
-    String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
-            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
-            @Nullable SuspendDialogInfo dialogInfo, @NonNull String callingPackage,
-            int userId, int callingUid) {
+    String[] setPackagesSuspended(@NonNull Computer computer, @Nullable String[] packageNames,
+            boolean suspended, @Nullable PersistableBundle appExtras,
+            @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
+            @NonNull String callingPackage, @UserIdInt int userId, int callingUid) {
         if (ArrayUtils.isEmpty(packageNames)) {
             return packageNames;
         }
@@ -112,7 +113,7 @@
         }
 
         final SuspendParams newSuspendParams =
-                SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+                new SuspendParams(dialogInfo, appExtras, launcherExtras);
 
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
@@ -121,63 +122,60 @@
 
         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;
-                }
-                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;
-                }
-                if (canSuspend != null && !canSuspend[i]) {
-                    unmodifiablePackages.add(packageName);
-                    continue;
-                }
+        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;
+            }
+            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;
+            }
+            if (canSuspend != null && !canSuspend[i]) {
+                unmodifiablePackages.add(packageName);
+                continue;
+            }
 
-                final WatchedArrayMap<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;
-                        }
+            final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
+                    packageState.getUserStateOrDefault(userId).getSuspendParams();
+            if (suspended) {
+                if (suspendParamsMap != null && suspendParamsMap.containsKey(packageName)) {
+                    final SuspendParams suspendParams = suspendParamsMap.get(packageName);
+                    // Skip if there's no changes
+                    if (suspendParams != null
+                            && 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 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()));
+        }
 
         mPm.commitPackageStateMutation(null, mutator -> {
             final int size = modifiedPackages.size();
@@ -219,29 +217,27 @@
      * @return The names of packages which are Unsuspendable.
      */
     @NonNull
-    String[] getUnsuspendablePackagesForUser(@NonNull String[] packageNames, int userId,
-            int callingUid) {
+    String[] getUnsuspendablePackagesForUser(@NonNull Computer computer,
+            @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) {
         if (!isSuspendAllowedForUser(userId, callingUid)) {
             Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
             return packageNames;
         }
         final ArraySet<String> unactionablePackages = new ArraySet<>();
-        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]);
-                }
+        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()]);
     }
 
@@ -283,45 +279,44 @@
      *                                   suspensions will be removed.
      * @param userId The user for which the changes are taking place.
      */
-    void removeSuspensionsBySuspendingPackage(@NonNull String[] packagesToChange,
+    void removeSuspensionsBySuspendingPackage(@NonNull Computer computer,
+            @NonNull String[] packagesToChange,
             @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
         final List<String> unsuspendedPackages = new ArrayList<>();
         final IntArray unsuspendedUids = new IntArray();
         final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
-        mPm.executeWithConsistentComputer(computer -> {
-            for (String packageName : packagesToChange) {
-                final PackageStateInternal packageState =
-                        computer.getPackageStateInternal(packageName);
-                final PackageUserStateInternal packageUserState = packageState == null
-                        ? null : packageState.getUserStateOrDefault(userId);
-                if (packageUserState == null || !packageUserState.isSuspended()) {
-                    continue;
-                }
+        for (String packageName : packagesToChange) {
+            final PackageStateInternal packageState =
+                    computer.getPackageStateInternal(packageName);
+            final PackageUserStateInternal packageUserState = packageState == null
+                    ? null : packageState.getUserStateOrDefault(userId);
+            if (packageUserState == null || !packageUserState.isSuspended()) {
+                continue;
+            }
 
-                WatchedArrayMap<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++;
+            WatchedArrayMap<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);
                     }
-                }
-
-                // Everything would be removed and package unsuspended
-                if (countRemoved == suspendParamsMap.size()) {
-                    unsuspendedPackages.add(packageState.getPackageName());
-                    unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+                    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.commitPackageStateMutation(null, mutator -> {
             for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index f87063a..9c74dd7 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -71,6 +71,14 @@
           "include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest"
         }
       ]
+    },
+    {
+      "name": "CtsSharedUserMigrationTestCases",
+      "options": [
+        {
+          "include-filter": "android.uidmigration.cts"
+        }
+      ]
     }
   ],
   "presubmit-large": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5c4d011..b7c55c5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -36,6 +36,7 @@
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
@@ -98,6 +99,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.StatsEvent;
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.TypedXmlPullParser;
@@ -608,6 +610,8 @@
                 if (mUms.mPm.isDeviceUpgrading()) {
                     mUms.cleanupPreCreatedUsers();
                 }
+
+                mUms.registerStatsCallbacks();
             }
         }
 
@@ -4279,6 +4283,56 @@
                         : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
     }
 
+    /** Register callbacks for statsd pulled atoms. */
+    private void registerStatsCallbacks() {
+        final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.USER_INFO,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
+    }
+
+    /** Writes a UserInfo pulled atom for each user on the device. */
+    private int onPullAtom(int atomTag, List<StatsEvent> data) {
+        if (atomTag != FrameworkStatsLog.USER_INFO) {
+            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
+            return android.app.StatsManager.PULL_SKIP;
+        }
+        final List<UserInfo> users = getUsersInternal(true, true, true);
+        final int size = users.size();
+        for (int idx = 0; idx < size; idx++) {
+            final UserInfo user = users.get(idx);
+            if (user.id == UserHandle.USER_SYSTEM) {
+                // Skip user 0. It's not interesting. We already know it exists, is running, and (if
+                // we know the device configuration) its userType.
+                continue;
+            }
+
+            final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+            final String userTypeCustom = (userTypeStandard ==
+                    FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
+                    user.userType : null;
+
+            boolean isUserRunningUnlocked;
+            synchronized (mUserStates) {
+                isUserRunningUnlocked =
+                        mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+            }
+
+            data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+                    user.id,
+                    userTypeStandard,
+                    userTypeCustom,
+                    user.flags,
+                    user.creationTime,
+                    user.lastLoggedInTime,
+                    isUserRunningUnlocked
+            ));
+        }
+        return android.app.StatsManager.PULL_SUCCESS;
+    }
+
     @VisibleForTesting
     UserData putUserInfo(UserInfo userInfo) {
         final UserData userData = new UserData();
diff --git a/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
new file mode 100644
index 0000000..90c9228
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+public class UserNeedsBadgingCache {
+
+    private final Object mLock = new Object();
+
+    // Cache of users who need badging.
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseBooleanArray mUserCache = new SparseBooleanArray();
+
+    @NonNull
+    private final UserManagerService mUserManager;
+
+    public UserNeedsBadgingCache(@NonNull UserManagerService userManager) {
+        mUserManager = userManager;
+    }
+
+    public void delete(@UserIdInt int userId) {
+        synchronized (mLock) {
+            mUserCache.delete(userId);
+        }
+    }
+
+    public boolean get(@UserIdInt int userId) {
+        synchronized (mLock) {
+            int index = mUserCache.indexOfKey(userId);
+            if (index >= 0) {
+                return mUserCache.valueAt(index);
+            }
+        }
+
+        final UserInfo userInfo;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            userInfo = mUserManager.getUserInfo(userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        final boolean b;
+        b = userInfo != null && userInfo.isManagedProfile();
+        synchronized (mLock) {
+            mUserCache.put(userId, b);
+        }
+        return b;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/WatchedIntentResolver.java b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
index 1c3d884..daa50ab 100644
--- a/services/core/java/com/android/server/pm/WatchedIntentResolver.java
+++ b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
@@ -18,8 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.IntentFilter;
 
 import com.android.server.IntentResolver;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchableImpl;
@@ -99,8 +101,8 @@
     }
 
     @Override
-    public void addFilter(F f) {
-        super.addFilter(f);
+    public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
+        super.addFilter(snapshot, f);
         f.registerObserver(mWatcher);
         onChanged();
     }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 60d2fc1..49553f4 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -740,7 +740,7 @@
         grantPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm,
                         DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
-                userId, CONTACTS_PERMISSIONS);
+                userId, CONTACTS_PERMISSIONS, NOTIFICATION_PERMISSIONS);
 
         // Maps
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
@@ -775,7 +775,7 @@
                 grantPermissionsToSystemPackage(pm, voiceInteractPackageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
-                        NEARBY_DEVICES_PERMISSIONS);
+                        NEARBY_DEVICES_PERMISSIONS, NOTIFICATION_PERMISSIONS);
             }
         }
 
@@ -784,7 +784,8 @@
             grantPermissionsToSystemPackage(pm,
                     getDefaultSystemHandlerActivityPackage(pm,
                             SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
-                    userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
+                    userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
+                    NOTIFICATION_PERMISSIONS);
         }
 
         // Voice recognition
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 ed47bfb7..87494a6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3364,7 +3364,7 @@
             // For updated system applications, a privileged/oem permission
             // is granted only if it had been defined by the original application.
             if (pkgSetting.getTransientState().isUpdatedSystemApp()) {
-                final PackageSetting disabledPs = mPackageManagerInt
+                final PackageStateInternal disabledPs = mPackageManagerInt
                         .getDisabledSystemPackage(pkg.getPackageName());
                 final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
                 if (disabledPkg != null
@@ -3762,7 +3762,7 @@
             return;
         }
 
-        PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
+        PackageStateInternal disabledPs = mPackageManagerInt.getDisabledSystemPackage(
                 pkg.getPackageName());
         boolean isShadowingSystemPkg = disabledPs != null && disabledPs.getAppId() == pkg.getUid();
 
@@ -4104,7 +4104,7 @@
     }
 
     private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
-        final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+        final PackageStateInternal disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
                     permission.getPackageName());
         if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
             final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 410fa97..da22b17 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -37,6 +37,8 @@
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedArraySet;
 
+import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 
 @DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
@@ -540,6 +542,13 @@
         return this;
     }
 
+    @NonNull
+    @Override
+    public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
+        return mSharedLibraryOverlayPaths == null
+                ? Collections.emptyMap() : mSharedLibraryOverlayPaths;
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         // You can override field equality logic by defining either of the methods like:
@@ -703,11 +712,6 @@
     }
 
     @DataClass.Generated.Member
-    public @Nullable WatchedArrayMap<String,OverlayPaths> getSharedLibraryOverlayPaths() {
-        return mSharedLibraryOverlayPaths;
-    }
-
-    @DataClass.Generated.Member
     public @Nullable String getSplashScreenTheme() {
         return mSplashScreenTheme;
     }
@@ -774,10 +778,10 @@
     }
 
     @DataClass.Generated(
-            time = 1644638242940L,
+            time = 1645040852569L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
-            inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  boolean mInstalled\nprivate  boolean mStopped\nprivate  boolean mNotLaunched\nprivate  boolean mHidden\nprivate  int mDistractionFlags\nprivate  boolean mInstantApp\nprivate  boolean mVirtualPreload\nprivate  int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate  long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+            inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate  long mCeDataInode\nprivate  boolean mInstalled\nprivate  boolean mStopped\nprivate  boolean mNotLaunched\nprivate  boolean mHidden\nprivate  int mDistractionFlags\nprivate  boolean mInstantApp\nprivate  boolean mVirtualPreload\nprivate  int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate  long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate  void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic  boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic  void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic  com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic  com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index d24ce96..0926ba2 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -46,27 +46,13 @@
     private final PersistableBundle appExtras;
     private final PersistableBundle launcherExtras;
 
-    private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+    public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
             PersistableBundle launcherExtras) {
         this.dialogInfo = dialogInfo;
         this.appExtras = appExtras;
         this.launcherExtras = launcherExtras;
     }
 
-    /**
-     * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
-     * the fields are {@code null}.
-     *
-     * @return A {@link SuspendParams} object or {@code null}.
-     */
-    public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
-            PersistableBundle appExtras, PersistableBundle launcherExtras) {
-        if (dialogInfo == null && appExtras == null && launcherExtras == null) {
-            return null;
-        }
-        return new SuspendParams(dialogInfo, appExtras, launcherExtras);
-    }
-
     @Override
     public boolean equals(@Nullable Object obj) {
         if (this == obj) {
@@ -170,7 +156,7 @@
             Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
                     + " some fields may default", e);
         }
-        return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+        return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras);
     }
 
     public SuspendDialogInfo getDialogInfo() {
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index f199841..91d2010 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -887,7 +887,9 @@
                 | flag(pkg.hasRequestForegroundServiceExemption(),
                         ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
                 | flag(pkg.areAttributionsUserVisible(),
-                        ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE);
+                        ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE)
+                | flag(pkg.isOnBackInvokedCallbackEnabled(),
+                        ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK);
         // @formatter:on
         return privateFlagsExt;
     }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 1754877..f6f9faf 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -290,6 +290,9 @@
     /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */
     ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys);
 
+    /** @see R#styleable.AndroidManifest_sharedUserMaxSdkVersion */
+    ParsingPackage setLeavingSharedUid(boolean leavingSharedUid);
+
     ParsingPackage setLabelRes(int labelRes);
 
     ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
@@ -387,6 +390,8 @@
      */
     ParsingPackage setKnownActivityEmbeddingCerts(Set<String> knownActivityEmbeddingCerts);
 
+    ParsingPackage setOnBackInvokedCallbackEnabled(boolean enableOnBackInvokedCallback);
+
     // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
     //  for moving to the next step
     @CallSuper
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index c4b1275..6767027 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -548,6 +548,8 @@
         private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
         private static final long SDK_LIBRARY = 1L << 49;
         private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
+        private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 51;
+        private static final long LEAVING_SHARED_UID = 1L << 52;
     }
 
     private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2397,6 +2399,16 @@
     }
 
     @Override
+    public boolean isOnBackInvokedCallbackEnabled() {
+        return getBoolean(Booleans.ENABLE_ON_BACK_INVOKED_CALLBACK);
+    }
+
+    @Override
+    public boolean isLeavingSharedUid() {
+        return getBoolean(Booleans.LEAVING_SHARED_UID);
+    }
+
+    @Override
     public ParsingPackageImpl setBaseRevisionCode(int value) {
         baseRevisionCode = value;
         return this;
@@ -2545,6 +2557,11 @@
     }
 
     @Override
+    public ParsingPackageImpl setLeavingSharedUid(boolean value) {
+        return setBoolean(Booleans.LEAVING_SHARED_UID, value);
+    }
+
+    @Override
     public ParsingPackageImpl setLabelRes(int value) {
         labelRes = value;
         return this;
@@ -2995,4 +3012,10 @@
         mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
         return this;
     }
+
+    @Override
+    public ParsingPackage setOnBackInvokedCallbackEnabled(boolean value) {
+        setBoolean(Booleans.ENABLE_ON_BACK_INVOKED_CALLBACK, value);
+        return this;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
index 4b659a14..50033f6 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -355,4 +355,16 @@
      * @see R.styleable#AndroidManifest_inheritKeyStoreKeys
      */
     boolean shouldInheritKeyStoreKeys();
+
+    /**
+     * @see R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback
+     */
+    boolean isOnBackInvokedCallbackEnabled();
+
+    /**
+     * Returns true if R.styleable#AndroidManifest_sharedUserMaxSdkVersion is set to a value
+     * smaller than the current SDK version.
+     * @see R.styleable#AndroidManifest_sharedUserMaxSdkVersion
+     */
+    boolean isLeavingSharedUid();
 }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index a891980..ed1ab01 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -1032,11 +1032,6 @@
 
     private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
             ParsingPackage pkg, TypedArray sa) {
-        int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
-        if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) {
-            return input.success(pkg);
-        }
-
         String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
         if (TextUtils.isEmpty(str)) {
             return input.success(pkg);
@@ -1052,7 +1047,11 @@
             }
         }
 
+        int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
+        boolean leaving = (maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT);
+
         return input.success(pkg
+                .setLeavingSharedUid(leaving)
                 .setSharedUserId(str.intern())
                 .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
     }
@@ -2202,8 +2201,9 @@
                 .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
                 .setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa))
                 .setResetEnabledSettingsOnAppDataCleared(bool(false,
-                    R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
-                    sa))
+                        R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
+                        sa))
+                .setOnBackInvokedCallbackEnabled(bool(false, R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback, sa))
                 // targetSdkVersion gated
                 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
                 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
similarity index 70%
rename from services/core/java/com/android/server/pm/ComponentResolver.java
rename to services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index aa393d2..cf9370d 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.pm;
+package com.android.server.pm.resolution;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,11 +32,9 @@
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -46,14 +44,18 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
+import com.android.server.pm.Computer;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerException;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedActivity;
@@ -63,12 +65,13 @@
 import com.android.server.pm.pkg.component.ParsedProvider;
 import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
-import com.android.server.utils.WatchableImpl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -79,9 +82,7 @@
 import java.util.function.Function;
 
 /** Resolves all Android component types [activities, services, providers and receivers]. */
-public class ComponentResolver
-        extends WatchableImpl
-        implements Snappable {
+public class ComponentResolver extends ComponentResolverLocked implements Snappable {
     private static final boolean DEBUG = false;
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_FILTERS = false;
@@ -104,7 +105,7 @@
         PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
     }
 
-    static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
+    public static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
         int v1 = r1.priority;
         int v2 = r2.priority;
         //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
@@ -140,62 +141,8 @@
         return 0;
     };
 
-    private static UserManagerService sUserManager;
-    private static PackageManagerInternal sPackageManagerInternal;
-
-    /**
-     * Locking within package manager is going to get worse before it gets better. Currently,
-     * we need to share the {@link PackageManagerService} lock to prevent deadlocks. This occurs
-     * because in order to safely query the resolvers, we need to obtain this lock. However,
-     * during resolution, we call into the {@link PackageManagerService}. This is _not_ to
-     * operate on data controlled by the service proper, but, to check the state of package
-     * settings [contained in a {@link Settings} object]. However, the {@link Settings} object
-     * happens to be protected by the main {@link PackageManagerService} lock.
-     * <p>
-     * There are a couple potential solutions.
-     * <ol>
-     * <li>Split all of our locks into reader/writer locks. This would allow multiple,
-     * simultaneous read operations and means we don't have to be as cautious about lock
-     * layering. Only when we want to perform a write operation will we ever be in a
-     * position to deadlock the system.</li>
-     * <li>Use the same lock across all classes within the {@code com.android.server.pm}
-     * package. By unifying the lock object, we remove any potential lock layering issues
-     * within the package manager. However, we already have a sense that this lock is
-     * heavily contended and merely adding more dependencies on it will have further
-     * impact.</li>
-     * <li>Implement proper lock ordering within the package manager. By defining the
-     * relative layer of the component [eg. {@link PackageManagerService} is at the top.
-     * Somewhere in the middle would be {@link ComponentResolver}. At the very bottom
-     * would be {@link Settings}.] The ordering would allow higher layers to hold their
-     * lock while calling down. Lower layers must relinquish their lock before calling up.
-     * Since {@link Settings} would live at the lowest layer, the {@link ComponentResolver}
-     * would be able to hold its lock while checking the package setting state.</li>
-     * </ol>
-     */
-    private final PackageManagerTracedLock mLock;
-
-    /** All available activities, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ActivityIntentResolver mActivities;
-
-    /** All available providers, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ProviderIntentResolver mProviders;
-
-    /** All available receivers, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ReceiverIntentResolver mReceivers;
-
-    /** All available services, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ServiceIntentResolver mServices;
-
-    /** Mapping from provider authority [first directory in content URI codePath) to provider. */
-    @GuardedBy("mLock")
-    private final ArrayMap<String, ParsedProvider> mProvidersByAuthority;
-
     /** Whether or not processing protected filters should be deferred. */
-    private boolean mDeferProtectedFilters = true;
+    boolean mDeferProtectedFilters = true;
 
     /**
      * Tracks high priority intent filters for protected actions. During boot, certain
@@ -210,348 +157,62 @@
      * This is a pair of component package name to actual filter, because we don't store the
      * name inside the filter. It's technically independent of the component it's contained in.
      */
-    private List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
+    List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
 
-    ComponentResolver(UserManagerService userManager,
-            PackageManagerInternal packageManagerInternal,
-            PackageManagerTracedLock lock) {
-        sPackageManagerInternal = packageManagerInternal;
-        sUserManager = userManager;
-        mLock = lock;
-
-        mActivities = new ActivityIntentResolver();
-        mProviders = new ProviderIntentResolver();
-        mReceivers = new ReceiverIntentResolver();
-        mServices = new ServiceIntentResolver();
+    public ComponentResolver(@NonNull UserManagerService userManager,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+        super(userManager);
+        mActivities = new ActivityIntentResolver(userManager, userNeedsBadgingCache);
+        mProviders = new ProviderIntentResolver(userManager);
+        mReceivers = new ReceiverIntentResolver(userManager, userNeedsBadgingCache);
+        mServices = new ServiceIntentResolver(userManager);
         mProvidersByAuthority = new ArrayMap<>();
         mDeferProtectedFilters = true;
 
-        mSnapshot = new SnapshotCache<ComponentResolver>(this, this) {
+        mSnapshot = new SnapshotCache<ComponentResolverApi>(this, this) {
                 @Override
-                public ComponentResolver createSnapshot() {
-                    return new ComponentResolver(mSource);
+                public ComponentResolverApi createSnapshot() {
+                    return new ComponentResolverSnapshot(ComponentResolver.this,
+                            userNeedsBadgingCache);
                 }};
     }
 
-    // Copy constructor used in creating snapshots.
-    private ComponentResolver(ComponentResolver orig) {
-        // Do not set the static variables that are set in the default constructor.   Do
-        // create a new object for the lock.  The snapshot is read-only, so a lock is not
-        // strictly required.  However, the current code is simpler if the lock exists,
-        // but does not contend with any outside class.
-        // TODO: make the snapshot lock-free
-        mLock = new PackageManagerTracedLock();
-
-        mActivities = new ActivityIntentResolver(orig.mActivities);
-        mProviders = new ProviderIntentResolver(orig.mProviders);
-        mReceivers = new ReceiverIntentResolver(orig.mReceivers);
-        mServices = new ServiceIntentResolver(orig.mServices);
-        mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
-        mDeferProtectedFilters = orig.mDeferProtectedFilters;
-        mProtectedFilters = (mProtectedFilters == null)
-                            ? null
-                            : new ArrayList<>(orig.mProtectedFilters);
-
-        mSnapshot = null;
-    }
-
-    final SnapshotCache<ComponentResolver> mSnapshot;
+    final SnapshotCache<ComponentResolverApi> mSnapshot;
 
     /**
      * Create a snapshot.
      */
-    public ComponentResolver snapshot() {
+    public ComponentResolverApi snapshot() {
         return mSnapshot.snapshot();
     }
 
-
-    /** Returns the given activity */
-    @Nullable
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public ParsedActivity getActivity(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mActivities.mActivities.get(component);
-        }
-    }
-
-    /** Returns the given provider */
-    @Nullable
-    ParsedProvider getProvider(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mProviders.mProviders.get(component);
-        }
-    }
-
-    /** Returns the given receiver */
-    @Nullable
-    ParsedActivity getReceiver(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mReceivers.mActivities.get(component);
-        }
-    }
-
-    /** Returns the given service */
-    @Nullable
-    ParsedService getService(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mServices.mServices.get(component);
-        }
-    }
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean componentExists(@NonNull ComponentName componentName) {
-        synchronized (mLock) {
-            ParsedMainComponent component = mActivities.mActivities.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            component = mReceivers.mActivities.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            component = mServices.mServices.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            return mProviders.mProviders.get(componentName) != null;
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
-            int userId) {
-        synchronized (mLock) {
-            return mActivities.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
-            List<ParsedActivity> activities, int userId) {
-        synchronized (mLock) {
-            return mActivities.queryIntentForPackage(
-                    intent, resolvedType, flags, activities, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mProviders.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags,
-            List<ParsedProvider> providers, int userId) {
-        synchronized (mLock) {
-            return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId);
-        }
-    }
-
-    @Nullable
-    List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, long flags,
-            int userId) {
-        if (!sUserManager.exists(userId)) {
-            return null;
-        }
-        List<ProviderInfo> providerList = null;
-        CachedApplicationInfoGenerator appInfoGenerator = null;
-        synchronized (mLock) {
-            for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
-                final ParsedProvider p = mProviders.mProviders.valueAt(i);
-                if (p.getAuthority() == null) {
-                    continue;
-                }
-
-                final PackageStateInternal ps =
-                        sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-                if (ps == null) {
-                    continue;
-                }
-
-                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-                if (pkg == null) {
-                    continue;
-                }
-
-                if (processName != null && (!p.getProcessName().equals(processName)
-                        || !UserHandle.isSameApp(pkg.getUid(), uid))) {
-                    continue;
-                }
-                // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
-                if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
-                    continue;
-                }
-                if (appInfoGenerator == null) {
-                    appInfoGenerator = new CachedApplicationInfoGenerator();
-                }
-                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-                final ApplicationInfo appInfo =
-                        appInfoGenerator.generate(pkg, flags, state, userId, ps);
-                if (appInfo == null) {
-                    continue;
-                }
-
-                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
-                        pkg, p, flags, state, appInfo, userId, ps);
-                if (info == null) {
-                    continue;
-                }
-                if (providerList == null) {
-                    providerList = new ArrayList<>(i + 1);
-                }
-                providerList.add(info);
-            }
-        }
-        return providerList;
-    }
-
-    @Nullable
-    ProviderInfo queryProvider(String authority, long flags, int userId) {
-        synchronized (mLock) {
-            final ParsedProvider p = mProvidersByAuthority.get(authority);
-            if (p == null) {
-                return null;
-            }
-            final PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-            if (pkg == null) {
-                return null;
-            }
-            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
-                    pkg, flags, state, userId, ps);
-            if (appInfo == null) {
-                return null;
-            }
-            return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
-        }
-    }
-
-    void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
-            int userId) {
-        synchronized (mLock) {
-            CachedApplicationInfoGenerator appInfoGenerator = null;
-            for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
-                final ParsedProvider p = mProvidersByAuthority.valueAt(i);
-                if (!p.isSyncable()) {
-                    continue;
-                }
-
-                final PackageStateInternal ps =
-                        sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-                if (ps == null) {
-                    continue;
-                }
-
-                final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-                if (pkg == null) {
-                    continue;
-                }
-
-                if (safeMode && !pkg.isSystem()) {
-                    continue;
-                }
-                if (appInfoGenerator == null) {
-                    appInfoGenerator = new CachedApplicationInfoGenerator();
-                }
-                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-                final ApplicationInfo appInfo =
-                        appInfoGenerator.generate(pkg, 0, state, userId, ps);
-                if (appInfo == null) {
-                    continue;
-                }
-
-                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
-                        pkg, p, 0, state, appInfo, userId, ps);
-                if (info == null) {
-                    continue;
-                }
-                outNames.add(mProvidersByAuthority.keyAt(i));
-                outInfo.add(info);
-            }
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mReceivers.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags,
-            List<ParsedActivity> receivers, int userId) {
-        synchronized (mLock) {
-            return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mServices.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags,
-            List<ParsedService> services, int userId) {
-        synchronized (mLock) {
-            return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId);
-        }
-    }
-
-    /** Returns {@code true} if the given activity is defined by some package */
-    boolean isActivityDefined(ComponentName component) {
-        synchronized (mLock) {
-            return mActivities.mActivities.get(component) != null;
-        }
-    }
-
-    /** Asserts none of the providers defined in the given package haven't already been defined. */
-    void assertProvidersNotDefined(AndroidPackage pkg) throws PackageManagerException {
-        synchronized (mLock) {
-            assertProvidersNotDefinedLocked(pkg);
-        }
-    }
-
     /** Add all components defined in the given package to the internal structures. */
-    void addAllComponents(AndroidPackage pkg, boolean chatty) {
+    public void addAllComponents(AndroidPackage pkg, boolean chatty,
+            @Nullable String setupWizardPackage, @NonNull Computer computer) {
         final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
         synchronized (mLock) {
-            addActivitiesLocked(pkg, newIntents, chatty);
-            addReceiversLocked(pkg, chatty);
-            addProvidersLocked(pkg, chatty);
-            addServicesLocked(pkg, chatty);
+            addActivitiesLocked(computer, pkg, newIntents, chatty);
+            addReceiversLocked(computer, pkg, chatty);
+            addProvidersLocked(computer, pkg, chatty);
+            addServicesLocked(computer, pkg, chatty);
             onChanged();
         }
-        // expect single setupwizard package
-        final String setupWizardPackage = ArrayUtils.firstOrNull(
-                sPackageManagerInternal.getKnownPackageNames(
-                        PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
 
         for (int i = newIntents.size() - 1; i >= 0; --i) {
             final Pair<ParsedActivity, ParsedIntentInfo> pair = newIntents.get(i);
-            final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
+            final PackageStateInternal disabledPkgSetting = computer
                     .getDisabledSystemPackage(pair.first.getPackageName());
             final AndroidPackage disabledPkg =
                     disabledPkgSetting == null ? null : disabledPkgSetting.getPkg();
             final List<ParsedActivity> systemActivities =
                     disabledPkg != null ? disabledPkg.getActivities() : null;
-            adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
+            adjustPriority(computer, systemActivities, pair.first, pair.second, setupWizardPackage);
             onChanged();
         }
     }
 
     /** Removes all components defined in the given package from the internal structures. */
-    void removeAllComponents(AndroidPackage pkg, boolean chatty) {
+    public void removeAllComponents(AndroidPackage pkg, boolean chatty) {
         synchronized (mLock) {
             removeAllComponentsLocked(pkg, chatty);
             onChanged();
@@ -562,7 +223,7 @@
      * Reprocess any protected filters that have been deferred. At this point, we've scanned
      * all of the filters defined on the /system partition and know the special components.
      */
-    void fixProtectedFilterPriorities() {
+    public void fixProtectedFilterPriorities(@Nullable String setupWizardPackage) {
         synchronized (mLock) {
             if (!mDeferProtectedFilters) {
                 return;
@@ -576,11 +237,6 @@
                     mProtectedFilters;
             mProtectedFilters = null;
 
-            // expect single setupwizard package
-            final String setupWizardPackage = ArrayUtils.firstOrNull(
-                sPackageManagerInternal.getKnownPackageNames(
-                    PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
-
             if (DEBUG_FILTERS && setupWizardPackage == null) {
                 Slog.i(TAG, "No setup wizard;"
                         + " All protected intents capped to priority 0");
@@ -615,7 +271,7 @@
         }
     }
 
-    void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
                             : "Activity Resolver Table:", "  ", packageName,
@@ -625,7 +281,7 @@
         }
     }
 
-    void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
                             : "Provider Resolver Table:", "  ", packageName,
@@ -635,7 +291,7 @@
         }
     }
 
-    void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
                             : "Receiver Resolver Table:", "  ", packageName,
@@ -645,7 +301,7 @@
         }
     }
 
-    void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
                             : "Service Resolver Table:", "  ", packageName,
@@ -655,7 +311,8 @@
         }
     }
 
-    void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpContentProviders(@NonNull Computer computer, PrintWriter pw,
+            DumpState dumpState, String packageName) {
         synchronized (mLock) {
             boolean printedSomething = false;
             for (ParsedProvider p : mProviders.mProviders.values()) {
@@ -695,7 +352,7 @@
                 pw.print("    ");
                 pw.println(p.toString());
 
-                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+                AndroidPackage pkg = computer.getPackage(p.getPackageName());
 
                 if (pkg != null) {
                     pw.print("      applicationInfo=");
@@ -705,7 +362,7 @@
         }
     }
 
-    void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
+    public void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
         synchronized (mLock) {
             if (dumpState.onTitlePrinted()) pw.println();
             pw.println("Service permissions:");
@@ -728,13 +385,13 @@
     }
 
     @GuardedBy("mLock")
-    private void addActivitiesLocked(AndroidPackage pkg,
+    private void addActivitiesLocked(@NonNull Computer computer, AndroidPackage pkg,
             List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
         final int activitiesSize = ArrayUtils.size(pkg.getActivities());
         StringBuilder r = null;
         for (int i = 0; i < activitiesSize; i++) {
             ParsedActivity a = pkg.getActivities().get(i);
-            mActivities.addActivity(a, "activity", newIntents);
+            mActivities.addActivity(computer, a, "activity", newIntents);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -750,12 +407,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addProvidersLocked(AndroidPackage pkg, boolean chatty) {
+    private void addProvidersLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int providersSize = ArrayUtils.size(pkg.getProviders());
         StringBuilder r = null;
         for (int i = 0; i < providersSize; i++) {
             ParsedProvider p = pkg.getProviders().get(i);
-            mProviders.addProvider(p);
+            mProviders.addProvider(computer, p);
             if (p.getAuthority() != null) {
                 String[] names = p.getAuthority().split(";");
 
@@ -814,12 +471,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
+    private void addReceiversLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int receiversSize = ArrayUtils.size(pkg.getReceivers());
         StringBuilder r = null;
         for (int i = 0; i < receiversSize; i++) {
             ParsedActivity a = pkg.getReceivers().get(i);
-            mReceivers.addActivity(a, "receiver", null);
+            mReceivers.addActivity(computer, a, "receiver", null);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -835,12 +492,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addServicesLocked(AndroidPackage pkg, boolean chatty) {
+    private void addServicesLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int servicesSize = ArrayUtils.size(pkg.getServices());
         StringBuilder r = null;
         for (int i = 0; i < servicesSize; i++) {
             ParsedService s = pkg.getServices().get(i);
-            mServices.addService(s);
+            mServices.addService(computer, s);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -945,8 +602,8 @@
      * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
      * allowed to obtain any priority on any action.
      */
-    private void adjustPriority(List<ParsedActivity> systemActivities, ParsedActivity activity,
-            ParsedIntentInfo intentInfo, String setupWizardPackage) {
+    private void adjustPriority(@NonNull Computer computer, List<ParsedActivity> systemActivities,
+            ParsedActivity activity, ParsedIntentInfo intentInfo, String setupWizardPackage) {
         // nothing to do; priority is fine as-is
         IntentFilter intentFilter = intentInfo.getIntentFilter();
         if (intentFilter.getPriority() <= 0) {
@@ -954,7 +611,7 @@
         }
 
         String packageName = activity.getPackageName();
-        AndroidPackage pkg = sPackageManagerInternal.getPackage(packageName);
+        AndroidPackage pkg = computer.getPackage(packageName);
 
         final boolean privilegedApp = pkg.isPrivileged();
         String className = activity.getClassName();
@@ -1231,28 +888,30 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void assertProvidersNotDefinedLocked(AndroidPackage pkg)
+    /** Asserts none of the providers defined in the given package haven't already been defined. */
+    public void assertProvidersNotDefined(@NonNull AndroidPackage pkg)
             throws PackageManagerException {
-        final int providersSize = ArrayUtils.size(pkg.getProviders());
-        int i;
-        for (i = 0; i < providersSize; i++) {
-            ParsedProvider p = pkg.getProviders().get(i);
-            if (p.getAuthority() != null) {
-                final String[] names = p.getAuthority().split(";");
-                for (int j = 0; j < names.length; j++) {
-                    if (mProvidersByAuthority.containsKey(names[j])) {
-                        final ParsedProvider other = mProvidersByAuthority.get(names[j]);
-                        final String otherPackageName =
-                                (other != null && other.getComponentName() != null)
-                                        ? other.getComponentName().getPackageName() : "?";
-                        // if we're installing over the same already-installed package, this is ok
-                        if (!otherPackageName.equals(pkg.getPackageName())) {
-                            throw new PackageManagerException(
-                                    INSTALL_FAILED_CONFLICTING_PROVIDER,
-                                    "Can't install because provider name " + names[j]
-                                            + " (in package " + pkg.getPackageName()
-                                            + ") is already used by " + otherPackageName);
+        synchronized (mLock) {
+            final int providersSize = ArrayUtils.size(pkg.getProviders());
+            int i;
+            for (i = 0; i < providersSize; i++) {
+                ParsedProvider p = pkg.getProviders().get(i);
+                if (p.getAuthority() != null) {
+                    final String[] names = p.getAuthority().split(";");
+                    for (int j = 0; j < names.length; j++) {
+                        if (mProvidersByAuthority.containsKey(names[j])) {
+                            final ParsedProvider other = mProvidersByAuthority.get(names[j]);
+                            final String otherPackageName =
+                                    (other != null && other.getComponentName() != null)
+                                            ? other.getComponentName().getPackageName() : "?";
+                            // if installing over the same already-installed package,this is ok
+                            if (!otherPackageName.equals(pkg.getPackageName())) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_CONFLICTING_PROVIDER,
+                                        "Can't install because provider name " + names[j]
+                                                + " (in package " + pkg.getPackageName()
+                                                + ") is already used by " + otherPackageName);
+                            }
                         }
                     }
                 }
@@ -1266,22 +925,31 @@
         private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
         private boolean mIsUpdatingMimeGroup = false;
 
+        @NonNull
+        protected final UserManagerService mUserManager;
+
         // Default constructor
-        MimeGroupsAwareIntentResolver() {
+        MimeGroupsAwareIntentResolver(@NonNull UserManagerService userManager) {
+            mUserManager = userManager;
         }
 
         // Copy constructor used in creating snapshots
-        MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig) {
+        MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig,
+                @NonNull UserManagerService userManager) {
+            mUserManager = userManager;
             copyFrom(orig);
             copyInto(mMimeGroupToFilter, orig.mMimeGroupToFilter);
             mIsUpdatingMimeGroup = orig.mIsUpdatingMimeGroup;
         }
 
         @Override
-        public void addFilter(F f) {
+        public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
             IntentFilter intentFilter = getIntentFilter(f);
-            applyMimeGroups(f);
-            super.addFilter(f);
+            // We assume Computer is available for this class and all subclasses. Because this class
+            // uses subclass method override to handle logic, the Computer parameter must be in the
+            // base, leading to this odd nullability.
+            applyMimeGroups((Computer) snapshot, f);
+            super.addFilter(snapshot, f);
 
             if (!mIsUpdatingMimeGroup) {
                 register_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
@@ -1309,7 +977,8 @@
          * @param mimeGroup MIME group to update
          * @return true, if any intent filters were changed due to this update
          */
-        public boolean updateMimeGroup(String packageName, String mimeGroup) {
+        public boolean updateMimeGroup(@NonNull Computer computer, String packageName,
+                String mimeGroup) {
             F[] filters = mMimeGroupToFilter.get(mimeGroup);
             int n = filters != null ? filters.length : 0;
 
@@ -1318,18 +987,18 @@
             F filter;
             for (int i = 0; i < n && (filter = filters[i]) != null; i++) {
                 if (isPackageForFilter(packageName, filter)) {
-                    hasChanges |= updateFilter(filter);
+                    hasChanges |= updateFilter(computer, filter);
                 }
             }
             mIsUpdatingMimeGroup = false;
             return hasChanges;
         }
 
-        private boolean updateFilter(F f) {
+        private boolean updateFilter(@NonNull Computer computer, F f) {
             IntentFilter filter = getIntentFilter(f);
             List<String> oldTypes = filter.dataTypes();
             removeFilter(f);
-            addFilter(f);
+            addFilter(computer, f);
             List<String> newTypes = filter.dataTypes();
             return !equalLists(oldTypes, newTypes);
         }
@@ -1350,16 +1019,18 @@
             return first.equals(second);
         }
 
-        private void applyMimeGroups(F f) {
+        private void applyMimeGroups(@NonNull Computer computer, F f) {
             IntentFilter filter = getIntentFilter(f);
 
             for (int i = filter.countMimeGroups() - 1; i >= 0; i--) {
-                List<String> mimeTypes = sPackageManagerInternal.getMimeGroup(
-                        f.first.getPackageName(), filter.getMimeGroup(i));
+                final PackageStateInternal packageState = computer.getPackageStateInternal(
+                        f.first.getPackageName());
 
-                for (int typeIndex = mimeTypes.size() - 1; typeIndex >= 0; typeIndex--) {
-                    String mimeType = mimeTypes.get(typeIndex);
+                Collection<String> mimeTypes = packageState == null
+                        ? Collections.emptyList() : packageState.getMimeGroups()
+                        .get(filter.getMimeGroup(i));
 
+                for (String mimeType : mimeTypes) {
                     try {
                         filter.addDynamicDataType(mimeType);
                     } catch (IntentFilter.MalformedMimeTypeException e) {
@@ -1370,50 +1041,74 @@
                 }
             }
         }
+
+        @Override
+        protected boolean isFilterStopped(@Nullable PackageStateInternal packageState,
+                @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
+                return true;
+            }
+
+            if (packageState == null || packageState.getPkg() == null) {
+                return false;
+            }
+
+            // System apps are never considered stopped for purposes of
+            // filtering, because there may be no way for the user to
+            // actually re-launch them.
+            return !packageState.isSystem()
+                    && packageState.getUserStateOrDefault(userId).isStopped();
+        }
     }
 
-    private static class ActivityIntentResolver
+    public static class ActivityIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
 
+        @NonNull
+        private UserNeedsBadgingCache mUserNeedsBadging;
+
         // Default constructor
-        ActivityIntentResolver() {
+        ActivityIntentResolver(@NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(userManager);
+            mUserNeedsBadging = userNeedsBadgingCache;
         }
 
         // Copy constructor used in creating snapshots
-        ActivityIntentResolver(ActivityIntentResolver orig) {
-            super(orig);
+        ActivityIntentResolver(@NonNull ActivityIntentResolver orig,
+                @NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(orig, userManager);
             mActivities.putAll(orig.mActivities);
-            mFlags = orig.mFlags;
+            mUserNeedsBadging = userNeedsBadgingCache;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            if (!sUserManager.exists(userId)) return null;
-            mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) return null;
+            long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
         }
 
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedActivity> packageActivities, int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedActivity> packageActivities,
+                int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             if (packageActivities == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int activitiesSize = packageActivities.size();
             ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut =
@@ -1431,10 +1126,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        protected void addActivity(ParsedActivity a, String type,
+        protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type,
                 List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
             mActivities.put(a.getComponentName(), a);
             if (DEBUG_SHOW_INFO) {
@@ -1455,7 +1151,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Activity " + a.getName());
                 }
-                addFilter(Pair.create(a, intent));
+                addFilter(computer, Pair.create(a, intent));
             }
         }
 
@@ -1497,11 +1193,6 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedActivity, ParsedIntentInfo> filter, int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedActivity, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
@@ -1517,43 +1208,35 @@
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedActivity, ParsedIntentInfo> pair,
-                int match, int userId) {
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedActivity, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
             ParsedActivity activity = pair.first;
             ParsedIntentInfo info = pair.second;
             IntentFilter intentFilter = info.getIntentFilter();
 
-            if (!sUserManager.exists(userId)) {
+            if (!mUserManager.exists(userId)) {
                 if (DEBUG) {
                     log("User doesn't exist", info, match, userId);
                 }
                 return null;
             }
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(activity.getPackageName());
-            if (pkg == null) {
-                return null;
-            }
-
-            if (!sPackageManagerInternal.isEnabledAndMatches(activity, mFlags, userId)) {
+            final PackageStateInternal packageState =
+                    computer.getPackageStateInternal(activity.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, activity, customFlags,
+                    userId)) {
                 if (DEBUG) {
-                    log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
-                            + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
+                    log("!PackageManagerInternal.isEnabledAndMatches; flags="
+                            + DebugUtils.flagsToString(PackageManager.class, "MATCH_", customFlags),
                             info, match, userId);
                 }
                 return null;
             }
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(activity.getPackageName());
-            if (ps == null) {
-                if (DEBUG) {
-                    log("info.activity.owner.mExtras == null", info, match, userId);
-                }
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
-                    userState, userId, ps);
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            ActivityInfo ai = PackageInfoUtils.generateActivityInfo(packageState.getPkg(), activity,
+                    customFlags, userState, userId, packageState);
             if (ai == null) {
                 if (DEBUG) {
                     log("Failed to create ActivityInfo based on " + activity, info, match,
@@ -1562,15 +1245,15 @@
                 return null;
             }
             final boolean matchExplicitlyVisibleOnly =
-                    (mFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+                    (customFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
             final boolean matchVisibleToInstantApp =
-                    (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+                    (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
             final boolean componentVisible =
                     matchVisibleToInstantApp
                     && intentFilter.isVisibleToInstantApp()
                     && (!matchExplicitlyVisibleOnly
                             || intentFilter.isExplicitlyVisibleToInstantApp());
-            final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+            final boolean matchInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp && !(componentVisible || userState.isInstantApp())) {
                 if (DEBUG) {
@@ -1595,7 +1278,7 @@
             }
             // throw out instant app filters if updates are available; will trigger
             // instant app resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 if (DEBUG) {
                     log("Instant app update is available", info, match, userId);
                 }
@@ -1604,7 +1287,7 @@
             final ResolveInfo res =
                     new ResolveInfo(intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE));
             res.activityInfo = ai;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = intentFilter;
             }
             res.handleAllWebDataURI = intentFilter.handleAllWebDataURI();
@@ -1617,7 +1300,7 @@
             res.isDefault = info.isHasDefault();
             res.labelRes = info.getLabelRes();
             res.nonLocalizedLabel = info.getNonLocalizedLabel();
-            if (sPackageManagerInternal.userNeedsBadging(userId)) {
+            if (mUserNeedsBadging.get(userId)) {
                 res.noResourceId = true;
             } else {
                 res.icon = info.getIcon();
@@ -1683,19 +1366,22 @@
         // ActivityIntentResolver.
         protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
                 new ArrayMap<>();
-        private long mFlags;
     }
 
     // Both receivers and activities share a class, but point to different get methods
-    private static final class ReceiverIntentResolver extends ActivityIntentResolver {
+    public static final class ReceiverIntentResolver extends ActivityIntentResolver {
 
         // Default constructor
-        ReceiverIntentResolver() {
+        ReceiverIntentResolver(@NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(userManager, userNeedsBadgingCache);
         }
 
         // Copy constructor used in creating snapshots
-        ReceiverIntentResolver(ReceiverIntentResolver orig) {
-            super(orig);
+        ReceiverIntentResolver(@NonNull ReceiverIntentResolver orig,
+                @NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(orig, userManager, userNeedsBadgingCache);
         }
 
         @Override
@@ -1704,48 +1390,50 @@
         }
     }
 
-    private static final class ProviderIntentResolver
+    public static final class ProviderIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
         // Default constructor
-        ProviderIntentResolver() {
+        ProviderIntentResolver(@NonNull UserManagerService userManager) {
+            super(userManager);
         }
 
         // Copy constructor used in creating snapshots
-        ProviderIntentResolver(ProviderIntentResolver orig) {
-            super(orig);
+        ProviderIntentResolver(@NonNull ProviderIntentResolver orig,
+                @NonNull UserManagerService userManager) {
+            super(orig, userManager);
             mProviders.putAll(orig.mProviders);
-            mFlags = orig.mFlags;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
-        }
-
-        @Nullable
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) {
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+            long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
         @Nullable
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedProvider> packageProviders, int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) {
+                return null;
+            }
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+        }
+
+        @Nullable
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedProvider> packageProviders,
+                int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             if (packageProviders == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int providersSize = packageProviders.size();
             ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut =
@@ -1763,10 +1451,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        void addProvider(ParsedProvider p) {
+        void addProvider(@NonNull Computer computer, ParsedProvider p) {
             if (mProviders.containsKey(p.getComponentName())) {
                 Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
                 return;
@@ -1789,7 +1478,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Provider " + p.getName());
                 }
-                addFilter(Pair.create(p, intent));
+                addFilter(computer, Pair.create(p, intent));
             }
         }
 
@@ -1832,21 +1521,16 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedProvider, ParsedIntentInfo> filter,
-                int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedProvider, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedProvider, ParsedIntentInfo> pair,
-                int match, int userId) {
-            if (!sUserManager.exists(userId)) {
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
 
@@ -1854,24 +1538,18 @@
             ParsedIntentInfo intentInfo = pair.second;
             IntentFilter filter = intentInfo.getIntentFilter();
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(provider.getPackageName());
-            if (pkg == null) {
+            PackageStateInternal packageState =
+                    computer.getPackageStateInternal(provider.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, provider, customFlags,
+                    userId)) {
                 return null;
             }
 
-            if (!sPackageManagerInternal.isEnabledAndMatches(provider, mFlags, userId)) {
-                return null;
-            }
-
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(provider.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            final boolean matchVisibleToInstantApp = (mFlags
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            final boolean matchVisibleToInstantApp = (customFlags
                     & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-            final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+            final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to instant applications
             if (matchVisibleToInstantApp
                     && !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -1883,22 +1561,22 @@
             }
             // throw out instant application filters if updates are available; will trigger
             // instant application resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 return null;
             }
             final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
-                    pkg, mFlags, userState, userId, ps);
+                    packageState.getPkg(), customFlags, userState, userId, packageState);
             if (appInfo == null) {
                 return null;
             }
-            ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
-                    userState, appInfo, userId, ps);
+            ProviderInfo pi = PackageInfoUtils.generateProviderInfo(packageState.getPkg(), provider,
+                    customFlags, userState, appInfo, userId, packageState);
             if (pi == null) {
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
             res.providerInfo = pi;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
             res.priority = filter.getPriority();
@@ -1959,46 +1637,46 @@
             return input.second.getIntentFilter();
         }
 
-        private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
-        private long mFlags;
+        final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
     }
 
-    private static final class ServiceIntentResolver
+    public static final class ServiceIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
         // Default constructor
-        ServiceIntentResolver() {
+        ServiceIntentResolver(@NonNull UserManagerService userManager) {
+            super(userManager);
         }
 
         // Copy constructor used in creating snapshots
-        ServiceIntentResolver(ServiceIntentResolver orig) {
-            copyFrom(orig);
+        ServiceIntentResolver(@NonNull ServiceIntentResolver orig,
+                @NonNull UserManagerService userManager) {
+            super(orig, userManager);
             mServices.putAll(orig.mServices);
-            mFlags = orig.mFlags;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
+                return null;
+            }
+            long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) return null;
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) return null;
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
         }
 
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedService> packageServices, int userId) {
-            if (!sUserManager.exists(userId)) return null;
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedService> packageServices, int userId) {
+            if (!mUserManager.exists(userId)) return null;
             if (packageServices == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int servicesSize = packageServices.size();
             ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut =
@@ -2016,10 +1694,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        void addService(ParsedService s) {
+        void addService(@NonNull Computer computer, ParsedService s) {
             mServices.put(s.getComponentName(), s);
             if (DEBUG_SHOW_INFO) {
                 Log.v(TAG, "  service:");
@@ -2037,7 +1716,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Service " + s.getName());
                 }
-                addFilter(Pair.create(s, intent));
+                addFilter(computer, Pair.create(s, intent));
             }
         }
 
@@ -2080,48 +1759,38 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedService, ParsedIntentInfo> filter, int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedService, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedService, ParsedIntentInfo> pair, int match,
-                int userId) {
-            if (!sUserManager.exists(userId)) return null;
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) return null;
 
             ParsedService service = pair.first;
             ParsedIntentInfo intentInfo = pair.second;
             IntentFilter filter = intentInfo.getIntentFilter();
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(service.getPackageName());
-            if (pkg == null) {
+            final PackageStateInternal packageState = computer.getPackageStateInternal(
+                    service.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, service, customFlags,
+                    userId)) {
                 return null;
             }
 
-            if (!sPackageManagerInternal.isEnabledAndMatches(service, mFlags, userId)) {
-                return null;
-            }
-
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(service.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
-                    userState, userId, ps);
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            ServiceInfo si = PackageInfoUtils.generateServiceInfo(packageState.getPkg(), service,
+                    customFlags, userState, userId, packageState);
             if (si == null) {
                 return null;
             }
             final boolean matchVisibleToInstantApp =
-                    (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-            final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+                    (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+            final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp
                     && !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -2133,12 +1802,12 @@
             }
             // throw out instant app filters if updates are available; will trigger
             // instant app resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
             res.serviceInfo = si;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
             res.priority = filter.getPriority();
@@ -2203,11 +1872,10 @@
         }
 
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
-        private long mFlags;
+        final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
     }
 
-    static final class InstantAppIntentResolver
+    public static final class InstantAppIntentResolver
             extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
             AuxiliaryResolveInfo.AuxiliaryFilter> {
         /**
@@ -2224,6 +1892,13 @@
         final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult =
                 new ArrayMap<>();
 
+        @NonNull
+        private final UserManagerService mUserManager;
+
+        public InstantAppIntentResolver(@NonNull UserManagerService userManager) {
+            mUserManager = userManager;
+        }
+
         @Override
         protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) {
             return new AuxiliaryResolveInfo.AuxiliaryFilter[size];
@@ -2236,9 +1911,10 @@
         }
 
         @Override
-        protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(
-                AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) {
-            if (!sUserManager.exists(userId)) {
+        protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(@NonNull Computer computer,
+                AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             final String packageName = responseObj.resolveInfo.getPackageName();
@@ -2296,40 +1972,17 @@
         }
     }
 
-    private static boolean isFilterStopped(Pair<? extends ParsedComponent, ParsedIntentInfo> pair,
-            int userId) {
-        if (!sUserManager.exists(userId)) {
-            return true;
-        }
-
-        AndroidPackage pkg = sPackageManagerInternal.getPackage(pair.first.getPackageName());
-        if (pkg == null) {
-            return false;
-        }
-
-        PackageStateInternal ps =
-                sPackageManagerInternal.getPackageStateInternal(pair.first.getPackageName());
-        if (ps == null) {
-            return false;
-        }
-
-        // System apps are never considered stopped for purposes of
-        // filtering, because there may be no way for the user to
-        // actually re-launch them.
-        return !ps.isSystem() && ps.getUserStateOrDefault(userId).isStopped();
-    }
-
     /**
      * Removes MIME type from the group, by delegating to IntentResolvers
      * @return true if any intent filters were changed due to this update
      */
-    boolean updateMimeGroup(String packageName, String group) {
+    public boolean updateMimeGroup(@NonNull Computer computer, String packageName, String group) {
         boolean hasChanges = false;
         synchronized (mLock) {
-            hasChanges |= mActivities.updateMimeGroup(packageName, group);
-            hasChanges |= mProviders.updateMimeGroup(packageName, group);
-            hasChanges |= mReceivers.updateMimeGroup(packageName, group);
-            hasChanges |= mServices.updateMimeGroup(packageName, group);
+            hasChanges |= mActivities.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mProviders.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mReceivers.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mServices.updateMimeGroup(computer, packageName, group);
             if (hasChanges) {
                 onChanged();
             }
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
new file mode 100644
index 0000000..b6f2b2a0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -0,0 +1,100 @@
+/*
+ * 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public interface ComponentResolverApi {
+
+    boolean isActivityDefined(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedActivity getActivity(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedProvider getProvider(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedActivity getReceiver(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedService getService(@NonNull ComponentName component);
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    boolean componentExists(@NonNull ComponentName componentName);
+
+    @Nullable
+    List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            @UserIdInt int userId);
+
+    @Nullable
+    ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName,
+            @Nullable String metaDataKey, int uid, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId);
+
+    void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId);
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
new file mode 100644
index 0000000..6b50fc6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -0,0 +1,308 @@
+/*
+ * 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.parsing.PackageInfoUtils;
+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.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.utils.WatchableImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ComponentResolverBase extends WatchableImpl implements ComponentResolverApi {
+
+    @NonNull
+    protected ComponentResolver.ActivityIntentResolver mActivities;
+
+    @NonNull
+    protected ComponentResolver.ProviderIntentResolver mProviders;
+
+    @NonNull
+    protected ComponentResolver.ReceiverIntentResolver mReceivers;
+
+    @NonNull
+    protected ComponentResolver.ServiceIntentResolver mServices;
+
+    /** Mapping from provider authority [first directory in content URI codePath) to provider. */
+    @NonNull
+    protected ArrayMap<String, ParsedProvider> mProvidersByAuthority;
+
+    @NonNull
+    protected UserManagerService mUserManager;
+
+    protected ComponentResolverBase(@NonNull UserManagerService userManager) {
+        mUserManager = userManager;
+    }
+
+    @Override
+    public boolean componentExists(@NonNull ComponentName componentName) {
+        ParsedMainComponent component = mActivities.mActivities.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        component = mReceivers.mActivities.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        component = mServices.mServices.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        return mProviders.mProviders.get(componentName) != null;
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getActivity(@NonNull ComponentName component) {
+        return mActivities.mActivities.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedProvider getProvider(@NonNull ComponentName component) {
+        return mProviders.mProviders.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getReceiver(@NonNull ComponentName component) {
+        return mReceivers.mActivities.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedService getService(@NonNull ComponentName component) {
+        return mServices.mServices.get(component);
+    }
+
+    /**
+     * Returns {@code true} if the given activity is defined by some package
+     */
+    @Override
+    public boolean isActivityDefined(@NonNull ComponentName component) {
+        return mActivities.mActivities.get(component) != null;
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mActivities.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            int userId) {
+        return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+            long flags, int userId) {
+        final ParsedProvider p = mProvidersByAuthority.get(authority);
+        if (p == null) {
+            return null;
+        }
+        PackageStateInternal packageState = computer.getPackageStateInternal(p.getPackageName());
+        if (packageState == null) {
+            return null;
+        }
+        final AndroidPackage pkg = packageState.getPkg();
+        if (pkg == null) {
+            return null;
+        }
+        final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId);
+        ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+                pkg, flags, state, userId, packageState);
+        if (appInfo == null) {
+            return null;
+        }
+        return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId,
+                packageState);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mProviders.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId) {
+        return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+            @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+            int userId) {
+        if (!mUserManager.exists(userId)) {
+            return null;
+        }
+        List<ProviderInfo> providerList = null;
+        PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+        for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
+            final ParsedProvider p = mProviders.mProviders.valueAt(i);
+            if (p.getAuthority() == null) {
+                continue;
+            }
+
+            final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+            if (ps == null) {
+                continue;
+            }
+
+            AndroidPackage pkg = ps.getPkg();
+            if (pkg == null) {
+                continue;
+            }
+
+            if (processName != null && (!p.getProcessName().equals(processName)
+                    || !UserHandle.isSameApp(pkg.getUid(), uid))) {
+                continue;
+            }
+            // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
+            if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
+                continue;
+            }
+            if (appInfoGenerator == null) {
+                appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+            }
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+            final ApplicationInfo appInfo =
+                    appInfoGenerator.generate(pkg, flags, state, userId, ps);
+            if (appInfo == null) {
+                continue;
+            }
+
+            final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                    pkg, p, flags, state, appInfo, userId, ps);
+            if (info == null) {
+                continue;
+            }
+            if (providerList == null) {
+                providerList = new ArrayList<>(i + 1);
+            }
+            providerList.add(info);
+        }
+        return providerList;
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId) {
+        return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        return mServices.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId) {
+        return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services,
+                userId);
+    }
+
+    @Override
+    public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, int userId) {
+        PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+        for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
+            final ParsedProvider p = mProvidersByAuthority.valueAt(i);
+            if (!p.isSyncable()) {
+                continue;
+            }
+
+            final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+            if (ps == null) {
+                continue;
+            }
+
+            final AndroidPackage pkg = ps.getPkg();
+            if (pkg == null) {
+                continue;
+            }
+
+            if (safeMode && !pkg.isSystem()) {
+                continue;
+            }
+            if (appInfoGenerator == null) {
+                appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+            }
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+            final ApplicationInfo appInfo =
+                    appInfoGenerator.generate(pkg, 0, state, userId, ps);
+            if (appInfo == null) {
+                continue;
+            }
+
+            final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                    pkg, p, 0, state, appInfo, userId, ps);
+            if (info == null) {
+                continue;
+            }
+            outNames.add(mProvidersByAuthority.keyAt(i));
+            outInfo.add(info);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
new file mode 100644
index 0000000..ecc53eb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.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 com.android.server.pm.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.PackageManagerTracedLock;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public abstract class ComponentResolverLocked extends ComponentResolverBase {
+
+    protected final PackageManagerTracedLock mLock = new PackageManagerTracedLock();
+
+    protected ComponentResolverLocked(@NonNull UserManagerService userManager) {
+        super(userManager);
+    }
+
+    @Override
+    public boolean componentExists(@NonNull ComponentName componentName) {
+        synchronized (mLock) {
+            return super.componentExists(componentName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getActivity(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getActivity(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedProvider getProvider(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getProvider(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getReceiver(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getReceiver(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedService getService(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getService(component);
+        }
+    }
+
+    @Override
+    public boolean isActivityDefined(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.isActivityDefined(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryActivities(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryActivities(computer, intent, resolvedType, flags, activities, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+            long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProvider(computer, authority, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, intent, resolvedType, flags, providers, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+            @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, processName, metaDataKey, uid, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryReceivers(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryServices(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryServices(computer, intent, resolvedType, flags, services, userId);
+        }
+    }
+
+    @Override
+    public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId) {
+        synchronized (mLock) {
+            super.querySyncProviders(computer, outNames, outInfo, safeMode, userId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
new file mode 100644
index 0000000..3a96fe6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
@@ -0,0 +1,38 @@
+/*
+ * 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.resolution;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
+
+public class ComponentResolverSnapshot extends ComponentResolverBase {
+
+    public ComponentResolverSnapshot(@NonNull ComponentResolver orig,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+        super(UserManagerService.getInstance());
+        mActivities = new ComponentResolver.ActivityIntentResolver(orig.mActivities, mUserManager,
+                userNeedsBadgingCache);
+        mProviders = new ComponentResolver.ProviderIntentResolver(orig.mProviders, mUserManager);
+        mReceivers = new ComponentResolver.ReceiverIntentResolver(orig.mReceivers, mUserManager,
+                userNeedsBadgingCache);
+        mServices = new ComponentResolver.ServiceIntentResolver(orig.mServices, mUserManager);
+        mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
+    }
+}
diff --git a/apex/media/aidl/stable/android/media/Session2Token.aidl b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
similarity index 79%
copy from apex/media/aidl/stable/android/media/Session2Token.aidl
copy to services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
index c5980e9..b091445 100644
--- a/apex/media/aidl/stable/android/media/Session2Token.aidl
+++ b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 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,6 +14,7 @@
  * limitations under the License.
  */
 
-package android.media;
+package com.android.server.pm.snapshot;
 
-parcelable Session2Token;
+public interface PackageDataSnapshot {
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index b730ab2..e06b608 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -30,6 +30,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.Computer;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
@@ -38,7 +39,6 @@
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.function.Function;
 
 @SuppressWarnings("PointlessBooleanExpression")
 public class DomainVerificationDebug {
@@ -62,8 +62,7 @@
     }
 
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction,
+            @Nullable @UserIdInt Integer userId, @NonNull Computer snapshot,
             @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
             throws NameNotFoundException {
         ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -74,7 +73,7 @@
             for (int index = 0; index < size; index++) {
                 DomainVerificationPkgState pkgState = stateMap.valueAt(index);
                 String pkgName = pkgState.getPackageName();
-                PackageStateInternal pkgSetting = pkgSettingFunction.apply(pkgName);
+                PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
                 if (pkgSetting == null || pkgSetting.getPkg() == null) {
                     continue;
                 }
@@ -90,7 +89,7 @@
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
             if (pkgSetting == null || pkgSetting.getPkg() == null) {
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 25147d0..1714086 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -25,7 +25,6 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageSettingsSnapshotProvider;
 import android.content.pm.ResolveInfo;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
@@ -37,7 +36,7 @@
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 
-import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.Computer;
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.Settings;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -227,14 +226,14 @@
      * assumed nothing has changed since the device rebooted.
      * <p>
      * If this is a new install, state will be restored from a previous call to {@link
-     * #restoreSettings(TypedXmlPullParser)}, or a new one will be generated. In either case, a
+     * #restoreSettings(Computer, TypedXmlPullParser)}, or a new one will be generated. In either case, a
      * broadcast will be sent to the domain verification agent so it may re-run any verification
      * logic for the newly associated domains.
      * <p>
      * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
      * lock. This should never be called from within the domain verification classes themselves.
      * <p>
-     * This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
+     * This will NOT call {@link #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. That must be
      * handled by the caller.
      */
     void addPackage(@NonNull PackageStateInternal newPkgSetting);
@@ -249,7 +248,7 @@
      * This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
      * lock. This should never be called from within the domain verification classes themselves.
      * <p>
-     * This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
+     * This will NOT call {@link #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. That must be
      * handled by the caller.
      */
     void migrateState(@NonNull PackageStateInternal oldPkgSetting,
@@ -259,24 +258,24 @@
      * Serializes the entire internal state. This is equivalent to a full backup of the existing
      * verification state. This write includes legacy state, as a sibling tag the modern state.
      *
+     * @param snapshot
      * @param includeSignatures Whether to include the package signatures in the output, mainly
      *                          used for backing up the user settings and ensuring they're
      *                          re-attached to the same package.
      * @param userId The user to write out. Supports {@link UserHandle#USER_ALL} if all users
-     *               should be written.
      */
-    void writeSettings(@NonNull TypedXmlSerializer serializer, boolean includeSignatures,
-            @UserIdInt int userId) throws IOException;
+    void writeSettings(@NonNull Computer snapshot, @NonNull TypedXmlSerializer serializer,
+            boolean includeSignatures, @UserIdInt int userId) throws IOException;
 
     /**
      * Read back a list of {@link DomainVerificationPkgState}s previously written by {@link
-     * #writeSettings(TypedXmlSerializer, boolean, int)}. Assumes that the
+     * #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. Assumes that the
      * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} tag has already been entered.
      * <p>
      * This is expected to only be used to re-attach states for packages already known to be on the
-     * device. If restoring from a backup, use {@link #restoreSettings(TypedXmlPullParser)}.
+     * device. If restoring from a backup, use {@link #restoreSettings(Computer, TypedXmlPullParser)}.
      */
-    void readSettings(@NonNull TypedXmlPullParser parser)
+    void readSettings(@NonNull Computer snapshot, @NonNull TypedXmlPullParser parser)
             throws IOException, XmlPullParserException;
 
     /**
@@ -306,7 +305,7 @@
 
     /**
      * Restore a list of {@link DomainVerificationPkgState}s previously written by {@link
-     * #writeSettings(TypedXmlSerializer, boolean, int)}. Assumes that the
+     * #writeSettings(Computer, TypedXmlSerializer, boolean, int)}. Assumes that the
      * {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS}
      * tag has already been entered.
      * <p>
@@ -321,7 +320,7 @@
      * TODO(b/170746586): Figure out how to verify that package signatures match at snapshot time
      *  and restore time.
      */
-    void restoreSettings(@NonNull TypedXmlPullParser parser)
+    void restoreSettings(@NonNull Computer snapshot, @NonNull TypedXmlPullParser parser)
             throws IOException, XmlPullParserException;
 
     /**
@@ -349,17 +348,14 @@
     /**
      * Print the verification state and user selection state of a package.
      *
+     * @param snapshot
      * @param packageName        the package whose state to change, or all packages if none is
      *                           specified
      * @param userId             the specific user to print, or null to skip printing user selection
-     *                           states, supports {@link android.os.UserHandle#USER_ALL}
-     * @param pkgSettingFunction the method by which to retrieve package data; if this is called
-     *                           from {@link PackageManagerService}, it is expected to pass in the
-     *                           snapshot of {@link PackageStateInternal} objects
+ *                           states, supports {@link UserHandle#USER_ALL}
      */
-    void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
+    void printState(@NonNull Computer snapshot, @NonNull IndentingPrintWriter writer,
+            @Nullable String packageName, @Nullable @UserIdInt Integer userId)
             throws NameNotFoundException;
 
     @NonNull
@@ -406,12 +402,11 @@
             @NonNull Set<String> domains, int state) throws NameNotFoundException;
 
 
-    interface Connection extends DomainVerificationEnforcer.Callback,
-            PackageSettingsSnapshotProvider {
+    interface Connection extends DomainVerificationEnforcer.Callback {
 
         /**
          * Notify that a settings change has been made and that eventually
-         * {@link #writeSettings(TypedXmlSerializer, boolean, int)} should be invoked by the parent.
+         * {@link #writeSettings(Computer, TypedXmlSerializer, boolean, int)} should be invoked by the parent.
          */
         void scheduleWriteSettings();
 
@@ -433,5 +428,8 @@
 
         @UserIdInt
         int[] getAllUserIds();
+
+        @NonNull
+        Computer snapshot();
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index d6c89f7..13218ea 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -37,7 +37,6 @@
 import android.content.pm.verify.domain.DomainVerificationState;
 import android.content.pm.verify.domain.DomainVerificationUserState;
 import android.content.pm.verify.domain.IDomainVerificationManager;
-import android.os.Build;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -52,11 +51,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.FunctionalUtils;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.Settings;
+import com.android.server.pm.Computer;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
@@ -80,7 +78,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 @SuppressLint("MissingPermission")
@@ -109,9 +106,9 @@
      * immediately attached once its available.
      * <p>
      * Generally this should be not accessed directly. Prefer calling {@link
-     * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer, Function)}.
+     * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer, Computer)}.
      *
-     * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer, Function)
+     * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer, Computer)
      **/
     @GuardedBy("mLock")
     @NonNull
@@ -178,12 +175,7 @@
 
     @Override
     public void setConnection(@NonNull Connection connection) {
-        if (Build.IS_USERDEBUG || Build.IS_ENG) {
-            mConnection = new LockSafeConnection(connection);
-        } else {
-            mConnection = connection;
-        }
-
+        mConnection = connection;
         mEnforcer.setCallback(mConnection);
     }
 
@@ -264,44 +256,43 @@
     public DomainVerificationInfo getDomainVerificationInfo(@NonNull String packageName)
             throws NameNotFoundException {
         mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
-        return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
-                AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
-                if (pkg == null) {
-                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
-                }
-
-                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-                if (pkgState == null) {
-                    return null;
-                }
-
-                ArrayMap<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
-
-                // TODO(b/159952358): Should the domain list be cached?
-                ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
-                if (domains.isEmpty()) {
-                    return null;
-                }
-
-                int size = domains.size();
-                for (int index = 0; index < size; index++) {
-                    hostToStateMap.putIfAbsent(domains.valueAt(index),
-                            DomainVerificationState.STATE_NO_RESPONSE);
-                }
-
-                final int mapSize = hostToStateMap.size();
-                for (int index = 0; index < mapSize; index++) {
-                    int internalValue = hostToStateMap.valueAt(index);
-                    int publicValue = DomainVerificationState.convertToInfoState(internalValue);
-                    hostToStateMap.setValueAt(index, publicValue);
-                }
-
-                // TODO(b/159952358): Do not return if no values are editable (all ignored states)?
-                return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
+            AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
+            if (pkg == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
-        });
+
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
+            ArrayMap<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
+
+            // TODO(b/159952358): Should the domain list be cached?
+            ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
+            if (domains.isEmpty()) {
+                return null;
+            }
+
+            int size = domains.size();
+            for (int index = 0; index < size; index++) {
+                hostToStateMap.putIfAbsent(domains.valueAt(index),
+                        DomainVerificationState.STATE_NO_RESPONSE);
+            }
+
+            final int mapSize = hostToStateMap.size();
+            for (int index = 0; index < mapSize; index++) {
+                int internalValue = hostToStateMap.valueAt(index);
+                int publicValue = DomainVerificationState.convertToInfoState(internalValue);
+                hostToStateMap.setValueAt(index, publicValue);
+            }
+
+            // TODO(b/159952358): Do not return if no values are editable (all ignored states)?
+            return new DomainVerificationInfo(pkgState.getId(), packageName, hostToStateMap);
+        }
     }
 
     @DomainVerificationManager.Error
@@ -324,42 +315,40 @@
             @NonNull Set<String> domains, int state)
             throws NameNotFoundException {
         mEnforcer.assertApprovedVerifier(callingUid, mProxy);
-        return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                List<String> verifiedDomains = new ArrayList<>();
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            List<String> verifiedDomains = new ArrayList<>();
 
-                GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
-                        true /* forAutoVerify */, callingUid, null /* userId */,
-                        pkgSettings);
-                if (result.isError()) {
-                    return result.getErrorCode();
-                }
-
-                DomainVerificationPkgState pkgState = result.getPkgState();
-                ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
-                for (String domain : domains) {
-                    Integer previousState = stateMap.get(domain);
-                    if (previousState != null
-                            && !DomainVerificationState.isModifiable(previousState)) {
-                        continue;
-                    }
-
-                    if (DomainVerificationState.isVerified(state)) {
-                        verifiedDomains.add(domain);
-                    }
-
-                    stateMap.put(domain, state);
-                }
-
-                int size = verifiedDomains.size();
-                for (int index = 0; index < size; index++) {
-                    removeUserStatesForDomain(verifiedDomains.get(index));
-                }
+            GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
+                    true /* forAutoVerify */, callingUid, null /* userId */, snapshot);
+            if (result.isError()) {
+                return result.getErrorCode();
             }
 
-            mConnection.scheduleWriteSettings();
-            return DomainVerificationManager.STATUS_OK;
-        });
+            DomainVerificationPkgState pkgState = result.getPkgState();
+            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+            for (String domain : domains) {
+                Integer previousState = stateMap.get(domain);
+                if (previousState != null
+                        && !DomainVerificationState.isModifiable(previousState)) {
+                    continue;
+                }
+
+                if (DomainVerificationState.isVerified(state)) {
+                    verifiedDomains.add(domain);
+                }
+
+                stateMap.put(domain, state);
+            }
+
+            int size = verifiedDomains.size();
+            for (int index = 0; index < size; index++) {
+                removeUserStatesForDomain(verifiedDomains.get(index));
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+        return DomainVerificationManager.STATUS_OK;
     }
 
     @Override
@@ -380,60 +369,30 @@
 
         ArraySet<String> verifiedDomains = new ArraySet<>();
         if (packageName == null) {
-            mConnection.withPackageSettingsSnapshot(pkgSettings -> {
-                synchronized (mLock) {
-                    ArraySet<String> validDomains = new ArraySet<>();
+            synchronized (mLock) {
+                final Computer snapshot = mConnection.snapshot();
+                ArraySet<String> validDomains = new ArraySet<>();
 
-                    int size = mAttachedPkgStates.size();
-                    for (int index = 0; index < size; index++) {
-                        DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
-                        String pkgName = pkgState.getPackageName();
-                        PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
-                        if (pkgSetting == null || pkgSetting.getPkg() == null) {
-                            continue;
-                        }
-
-                        AndroidPackage pkg = pkgSetting.getPkg();
-
-                        validDomains.clear();
-
-                        ArraySet<String> autoVerifyDomains =
-                                mCollector.collectValidAutoVerifyDomains(pkg);
-                        if (domains == null) {
-                            validDomains.addAll(autoVerifyDomains);
-                        } else {
-                            validDomains.addAll(domains);
-                            validDomains.retainAll(autoVerifyDomains);
-                        }
-
-                        if (DomainVerificationState.isVerified(state)) {
-                            verifiedDomains.addAll(validDomains);
-                        }
-
-                        setDomainVerificationStatusInternal(pkgState, state, validDomains);
-                    }
-                }
-            });
-        } else {
-            mConnection.withPackageSettingsSnapshotThrowing(pkgSettings -> {
-                synchronized (mLock) {
-                    DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-                    if (pkgState == null) {
-                        throw DomainVerificationUtils.throwPackageUnavailable(packageName);
-                    }
-
-                    PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    String pkgName = pkgState.getPackageName();
+                    PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
                     if (pkgSetting == null || pkgSetting.getPkg() == null) {
-                        throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                        continue;
                     }
 
                     AndroidPackage pkg = pkgSetting.getPkg();
-                    final ArraySet<String> validDomains;
+
+                    validDomains.clear();
+
+                    ArraySet<String> autoVerifyDomains =
+                            mCollector.collectValidAutoVerifyDomains(pkg);
                     if (domains == null) {
-                        validDomains = mCollector.collectValidAutoVerifyDomains(pkg);
+                        validDomains.addAll(autoVerifyDomains);
                     } else {
-                        validDomains = domains;
-                        validDomains.retainAll(mCollector.collectValidAutoVerifyDomains(pkg));
+                        validDomains.addAll(domains);
+                        validDomains.retainAll(autoVerifyDomains);
                     }
 
                     if (DomainVerificationState.isVerified(state)) {
@@ -442,7 +401,35 @@
 
                     setDomainVerificationStatusInternal(pkgState, state, validDomains);
                 }
-            });
+            }
+        } else {
+            synchronized (mLock) {
+                final Computer snapshot = mConnection.snapshot();
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+                if (pkgState == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
+                if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+                }
+
+                AndroidPackage pkg = pkgSetting.getPkg();
+                final ArraySet<String> validDomains;
+                if (domains == null) {
+                    validDomains = mCollector.collectValidAutoVerifyDomains(pkg);
+                } else {
+                    validDomains = domains;
+                    validDomains.retainAll(mCollector.collectValidAutoVerifyDomains(pkg));
+                }
+
+                if (DomainVerificationState.isVerified(state)) {
+                    verifiedDomains.addAll(validDomains);
+                }
+
+                setDomainVerificationStatusInternal(pkgState, state, validDomains);
+            }
         }
 
         // Mirror SystemApi behavior of revoking user selection for approved domains.
@@ -552,39 +539,38 @@
             return DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID;
         }
 
-        return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
-                        false /* forAutoVerify */, callingUid, userId, pkgSettings);
-                if (result.isError()) {
-                    return result.getErrorCode();
-                }
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
+                    false /* forAutoVerify */, callingUid, userId, snapshot);
+            if (result.isError()) {
+                return result.getErrorCode();
+            }
 
-                DomainVerificationPkgState pkgState = result.getPkgState();
-                DomainVerificationInternalUserState userState = pkgState.getOrCreateUserState(
-                        userId);
+            DomainVerificationPkgState pkgState = result.getPkgState();
+            DomainVerificationInternalUserState userState = pkgState.getOrCreateUserState(
+                    userId);
 
-                // Disable other packages if approving this one. Note that this check is only done
-                // for enabling. This allows an escape hatch in case multiple packages somehow get
-                // selected. They can be disabled without blocking in a circular dependency.
-                if (enabled) {
-                    int statusCode = revokeOtherUserSelectionsLocked(userState, userId, domains,
-                            pkgSettings);
-                    if (statusCode != DomainVerificationManager.STATUS_OK) {
-                        return statusCode;
-                    }
-                }
-
-                if (enabled) {
-                    userState.addHosts(domains);
-                } else {
-                    userState.removeHosts(domains);
+            // Disable other packages if approving this one. Note that this check is only done
+            // for enabling. This allows an escape hatch in case multiple packages somehow get
+            // selected. They can be disabled without blocking in a circular dependency.
+            if (enabled) {
+                int statusCode = revokeOtherUserSelectionsLocked(userState, userId, domains,
+                        snapshot);
+                if (statusCode != DomainVerificationManager.STATUS_OK) {
+                    return statusCode;
                 }
             }
 
-            mConnection.scheduleWriteSettings();
-            return DomainVerificationManager.STATUS_OK;
-        });
+            if (enabled) {
+                userState.addHosts(domains);
+            } else {
+                userState.removeHosts(domains);
+            }
+        }
+
+        mConnection.scheduleWriteSettings();
+        return DomainVerificationManager.STATUS_OK;
     }
 
     @Override
@@ -592,48 +578,47 @@
             @NonNull String packageName, boolean enabled, @Nullable ArraySet<String> domains)
             throws NameNotFoundException {
         mEnforcer.assertInternal(mConnection.getCallingUid());
-        mConnection.withPackageSettingsSnapshotThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-                if (pkgState == null) {
-                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
-                }
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
 
-                PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
-                AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
-                if (pkg == null) {
-                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
-                }
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
+            AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
+            if (pkg == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+            }
 
-                Set<String> validDomains =
-                        domains == null ? mCollector.collectAllWebDomains(pkg) : domains;
+            Set<String> validDomains =
+                    domains == null ? mCollector.collectAllWebDomains(pkg) : domains;
 
-                validDomains.retainAll(mCollector.collectAllWebDomains(pkg));
+            validDomains.retainAll(mCollector.collectAllWebDomains(pkg));
 
-                if (userId == UserHandle.USER_ALL) {
-                    for (int aUserId : mConnection.getAllUserIds()) {
-                        DomainVerificationInternalUserState userState =
-                                pkgState.getOrCreateUserState(aUserId);
-                        revokeOtherUserSelectionsLocked(userState, aUserId, validDomains,
-                                pkgSettings);
-                        if (enabled) {
-                            userState.addHosts(validDomains);
-                        } else {
-                            userState.removeHosts(validDomains);
-                        }
-                    }
-                } else {
+            if (userId == UserHandle.USER_ALL) {
+                for (int aUserId : mConnection.getAllUserIds()) {
                     DomainVerificationInternalUserState userState =
-                            pkgState.getOrCreateUserState(userId);
-                    revokeOtherUserSelectionsLocked(userState, userId, validDomains, pkgSettings);
+                            pkgState.getOrCreateUserState(aUserId);
+                    revokeOtherUserSelectionsLocked(userState, aUserId, validDomains,
+                            snapshot);
                     if (enabled) {
                         userState.addHosts(validDomains);
                     } else {
                         userState.removeHosts(validDomains);
                     }
                 }
+            } else {
+                DomainVerificationInternalUserState userState =
+                        pkgState.getOrCreateUserState(userId);
+                revokeOtherUserSelectionsLocked(userState, userId, validDomains, snapshot);
+                if (enabled) {
+                    userState.addHosts(validDomains);
+                } else {
+                    userState.removeHosts(validDomains);
+                }
             }
-        });
+        }
 
         mConnection.scheduleWriteSettings();
     }
@@ -641,8 +626,7 @@
     @GuardedBy("mLock")
     private int revokeOtherUserSelectionsLocked(
             @NonNull DomainVerificationInternalUserState userState, @UserIdInt int userId,
-            @NonNull Set<String> domains,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+            @NonNull Set<String> domains, @NonNull Computer snapshot) {
         // Cache the approved packages from the 1st pass because the search is expensive
         ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>();
 
@@ -652,7 +636,7 @@
             }
 
             Pair<List<String>, Integer> packagesToLevel = getApprovedPackagesLocked(domain,
-                    userId, APPROVAL_LEVEL_NONE + 1, pkgSettingFunction);
+                    userId, APPROVAL_LEVEL_NONE + 1, snapshot);
             int highestApproval = packagesToLevel.second;
             if (highestApproval > APPROVAL_LEVEL_SELECTION) {
                 return DomainVerificationManager.ERROR_UNABLE_TO_APPROVE;
@@ -698,51 +682,50 @@
             throw DomainVerificationUtils.throwPackageUnavailable(packageName);
         }
 
-        return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
-                AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
-                if (pkg == null) {
-                    throw DomainVerificationUtils.throwPackageUnavailable(packageName);
-                }
-
-                DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
-                if (pkgState == null) {
-                    return null;
-                }
-
-                ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
-                int webDomainsSize = webDomains.size();
-
-                Map<String, Integer> domains = new ArrayMap<>(webDomainsSize);
-                ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
-                DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
-                Set<String> enabledHosts =
-                        userState == null ? emptySet() : userState.getEnabledHosts();
-
-                for (int index = 0; index < webDomainsSize; index++) {
-                    String host = webDomains.valueAt(index);
-                    Integer state = stateMap.get(host);
-
-                    int domainState;
-                    if (state != null && DomainVerificationState.isVerified(state)) {
-                        domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
-                    } else if (enabledHosts.contains(host)) {
-                        domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
-                    } else {
-                        domainState = DomainVerificationUserState.DOMAIN_STATE_NONE;
-                    }
-
-                    domains.put(host, domainState);
-                }
-
-                boolean linkHandlingAllowed =
-                        userState == null || userState.isLinkHandlingAllowed();
-
-                return new DomainVerificationUserState(pkgState.getId(), packageName,
-                        UserHandle.of(userId), linkHandlingAllowed, domains);
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
+            AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
+            if (pkg == null) {
+                throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
-        });
+
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
+            if (pkgState == null) {
+                return null;
+            }
+
+            ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
+            int webDomainsSize = webDomains.size();
+
+            Map<String, Integer> domains = new ArrayMap<>(webDomainsSize);
+            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+            DomainVerificationInternalUserState userState = pkgState.getUserState(userId);
+            Set<String> enabledHosts =
+                    userState == null ? emptySet() : userState.getEnabledHosts();
+
+            for (int index = 0; index < webDomainsSize; index++) {
+                String host = webDomains.valueAt(index);
+                Integer state = stateMap.get(host);
+
+                int domainState;
+                if (state != null && DomainVerificationState.isVerified(state)) {
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
+                } else if (enabledHosts.contains(host)) {
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_SELECTED;
+                } else {
+                    domainState = DomainVerificationUserState.DOMAIN_STATE_NONE;
+                }
+
+                domains.put(host, domainState);
+            }
+
+            boolean linkHandlingAllowed =
+                    userState == null || userState.isLinkHandlingAllowed();
+
+            return new DomainVerificationUserState(pkgState.getId(), packageName,
+                    UserHandle.of(userId), linkHandlingAllowed, domains);
+        }
     }
 
     @NonNull
@@ -751,27 +734,26 @@
         mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
                 userId);
 
-        return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
-            SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false,
-                    userId, pkgSettings);
-            if (levelToPackages.size() == 0) {
-                return emptyList();
-            }
+        final Computer snapshot = mConnection.snapshot();
+        SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false,
+                userId, snapshot);
+        if (levelToPackages.size() == 0) {
+            return emptyList();
+        }
 
-            List<DomainOwner> owners = new ArrayList<>();
-            int size = levelToPackages.size();
-            for (int index = 0; index < size; index++) {
-                int level = levelToPackages.keyAt(index);
-                boolean overrideable = level <= APPROVAL_LEVEL_SELECTION;
-                List<String> packages = levelToPackages.valueAt(index);
-                int packagesSize = packages.size();
-                for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
-                    owners.add(new DomainOwner(packages.get(packageIndex), overrideable));
-                }
+        List<DomainOwner> owners = new ArrayList<>();
+        int size = levelToPackages.size();
+        for (int index = 0; index < size; index++) {
+            int level = levelToPackages.keyAt(index);
+            boolean overrideable = level <= APPROVAL_LEVEL_SELECTION;
+            List<String> packages = levelToPackages.valueAt(index);
+            int packagesSize = packages.size();
+            for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
+                owners.add(new DomainOwner(packages.get(packageIndex), overrideable));
             }
+        }
 
-            return owners;
-        });
+        return owners;
     }
 
     /**
@@ -782,8 +764,7 @@
      */
     @NonNull
     private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain,
-            boolean includeNegative, @UserIdInt int userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+            boolean includeNegative, @UserIdInt int userId, @NonNull Computer snapshot) {
         SparseArray<List<String>> levelToPackages = new SparseArray<>();
         // First, collect the raw approval level values
         synchronized (mLock) {
@@ -791,7 +772,7 @@
             for (int index = 0; index < size; index++) {
                 DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
                 String packageName = pkgState.getPackageName();
-                PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
+                PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
                 if (pkgSetting == null) {
                     continue;
                 }
@@ -818,8 +799,8 @@
         // Then sort them ascending by first installed time, with package name as tie breaker
         for (int index = 0; index < size; index++) {
             levelToPackages.valueAt(index).sort((first, second) -> {
-                PackageStateInternal firstPkgSetting = pkgSettingFunction.apply(first);
-                PackageStateInternal secondPkgSetting = pkgSettingFunction.apply(second);
+                PackageStateInternal firstPkgSetting = snapshot.getPackageStateInternal(first);
+                PackageStateInternal secondPkgSetting = snapshot.getPackageStateInternal(second);
 
                 long firstInstallTime = firstPkgSetting == null
                         ? -1L : firstPkgSetting.getUserStateOrDefault(userId).getFirstInstallTime();
@@ -1060,44 +1041,38 @@
     }
 
     @Override
-    public void writeSettings(@NonNull TypedXmlSerializer serializer, boolean includeSignatures,
-            @UserIdInt int userId)
-            throws IOException {
-        mConnection.withPackageSettingsSnapshotThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                Function<String, String> pkgNameToSignature = null;
-                if (includeSignatures) {
-                    pkgNameToSignature = pkgName -> {
-                        PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
-                        if (pkgSetting == null) {
-                            // If querying for a user restored package that isn't installed on the
-                            // device yet, there will be no signature to write out. In that case,
-                            // it's expected that this returns null and it falls back to the
-                            // restored state's stored signature if it exists.
-                            return null;
-                        }
+    public void writeSettings(Computer snapshot, @NonNull TypedXmlSerializer serializer,
+            boolean includeSignatures, @UserIdInt int userId) throws IOException {
+        synchronized (mLock) {
+            Function<String, String> pkgNameToSignature = null;
+            if (includeSignatures) {
+                pkgNameToSignature = pkgName -> {
+                    PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
+                    if (pkgSetting == null) {
+                        // If querying for a user restored package that isn't installed on the
+                        // device yet, there will be no signature to write out. In that case,
+                        // it's expected that this returns null and it falls back to the
+                        // restored state's stored signature if it exists.
+                        return null;
+                    }
 
-                        return PackageUtils.computeSignaturesSha256Digest(
-                                pkgSetting.getSigningDetails().getSignatures());
-                    };
-                }
-
-                mSettings.writeSettings(serializer, mAttachedPkgStates, userId, pkgNameToSignature);
+                    return PackageUtils.computeSignaturesSha256Digest(
+                            pkgSetting.getSigningDetails().getSignatures());
+                };
             }
-        });
+
+            mSettings.writeSettings(serializer, mAttachedPkgStates, userId, pkgNameToSignature);
+        }
 
         mLegacySettings.writeSettings(serializer);
     }
 
     @Override
-    public void readSettings(@NonNull TypedXmlPullParser parser) throws IOException,
-            XmlPullParserException {
-        mConnection.<IOException, XmlPullParserException>withPackageSettingsSnapshotThrowing2(
-                pkgSettings -> {
-                    synchronized (mLock) {
-                        mSettings.readSettings(parser, mAttachedPkgStates, pkgSettings);
-                    }
-                });
+    public void readSettings(@NonNull Computer snapshot, @NonNull TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            mSettings.readSettings(parser, mAttachedPkgStates, snapshot);
+        }
     }
 
     @Override
@@ -1107,14 +1082,11 @@
     }
 
     @Override
-    public void restoreSettings(@NonNull TypedXmlPullParser parser)
+    public void restoreSettings(Computer snapshot, @NonNull TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
-        mConnection.<IOException, XmlPullParserException>withPackageSettingsSnapshotThrowing2(
-                pkgSettings -> {
-                    synchronized (mLock) {
-                        mSettings.restoreSettings(parser, mAttachedPkgStates, pkgSettings);
-                    }
-                });
+        synchronized (mLock) {
+            mSettings.restoreSettings(parser, mAttachedPkgStates, snapshot);
+        }
     }
 
     @Override
@@ -1190,18 +1162,16 @@
     @Override
     public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
             @Nullable Integer userId) throws NameNotFoundException {
-        mConnection.withPackageSettingsSnapshotThrowing(
-                pkgSettings -> printState(writer, packageName, userId, pkgSettings));
+        printState(mConnection.snapshot(), writer, packageName, userId);
     }
 
     @Override
-    public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
-            @Nullable @UserIdInt Integer userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
+    public void printState(@NonNull Computer snapshot, @NonNull IndentingPrintWriter writer,
+            @Nullable String packageName, @Nullable @UserIdInt Integer userId)
             throws NameNotFoundException {
         mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
         synchronized (mLock) {
-            mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
+            mDebug.printState(writer, packageName, userId, snapshot, mAttachedPkgStates);
         }
     }
 
@@ -1209,31 +1179,30 @@
     public void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
             @Nullable String packageName, @Nullable @UserIdInt Integer userId)
             throws NameNotFoundException {
-        mConnection.withPackageSettingsSnapshotThrowing(pkgSettings -> {
-            synchronized (mLock) {
-                if (packageName == null) {
-                    int size = mAttachedPkgStates.size();
-                    for (int index = 0; index < size; index++) {
-                        try {
-                            printOwnersForPackage(writer,
-                                    mAttachedPkgStates.valueAt(index).getPackageName(), userId,
-                                    pkgSettings);
-                        } catch (NameNotFoundException ignored) {
-                            // When iterating packages, if one doesn't exist somehow, ignore
-                        }
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            if (packageName == null) {
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    try {
+                        printOwnersForPackage(writer,
+                                mAttachedPkgStates.valueAt(index).getPackageName(), userId,
+                                snapshot);
+                    } catch (NameNotFoundException ignored) {
+                        // When iterating packages, if one doesn't exist somehow, ignore
                     }
-                } else {
-                    printOwnersForPackage(writer, packageName, userId, pkgSettings);
                 }
+            } else {
+                printOwnersForPackage(writer, packageName, userId, snapshot);
             }
-        });
+        }
     }
 
     private void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
             @NonNull String packageName, @Nullable @UserIdInt Integer userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
+            @NonNull Computer snapshot)
             throws NameNotFoundException {
-        PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
+        PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
         AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
         if (pkg == null) {
             throw DomainVerificationUtils.throwPackageUnavailable(packageName);
@@ -1249,7 +1218,7 @@
         writer.increaseIndent();
 
         for (int index = 0; index < size; index++) {
-            printOwnersForDomain(writer, domains.valueAt(index), userId, pkgSettingFunction);
+            printOwnersForDomain(writer, domains.valueAt(index), userId, snapshot);
         }
 
         writer.decreaseIndent();
@@ -1258,30 +1227,28 @@
     @Override
     public void printOwnersForDomains(@NonNull IndentingPrintWriter writer,
             @NonNull List<String> domains, @Nullable @UserIdInt Integer userId) {
-        mConnection.withPackageSettingsSnapshot(pkgSettings -> {
-            synchronized (mLock) {
-                int size = domains.size();
-                for (int index = 0; index < size; index++) {
-                    printOwnersForDomain(writer, domains.get(index), userId, pkgSettings);
-                }
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            int size = domains.size();
+            for (int index = 0; index < size; index++) {
+                printOwnersForDomain(writer, domains.get(index), userId, snapshot);
             }
-        });
+        }
     }
 
     private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain,
-            @Nullable @UserIdInt Integer userId,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+            @Nullable @UserIdInt Integer userId, @NonNull Computer snapshot) {
         SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners =
                 new SparseArray<>();
 
         if (userId == null || userId == UserHandle.USER_ALL) {
             for (int aUserId : mConnection.getAllUserIds()) {
                 userIdToApprovalLevelToOwners.put(aUserId,
-                        getOwnersForDomainInternal(domain, true, aUserId, pkgSettingFunction));
+                        getOwnersForDomainInternal(domain, true, aUserId, snapshot));
             }
         } else {
             userIdToApprovalLevelToOwners.put(userId,
-                    getOwnersForDomainInternal(domain, true, userId, pkgSettingFunction));
+                    getOwnersForDomainInternal(domain, true, userId, snapshot));
         }
 
         mDebug.printOwners(writer, domain, userIdToApprovalLevelToOwners);
@@ -1330,8 +1297,7 @@
     @GuardedBy("mLock")
     private GetAttachedResult getAndValidateAttachedLocked(@NonNull UUID domainSetId,
             @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
-            @Nullable Integer userIdForFilter,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
+            @Nullable Integer userIdForFilter, @NonNull Computer snapshot)
             throws NameNotFoundException {
         if (domainSetId == null) {
             throw new IllegalArgumentException("domainSetId cannot be null");
@@ -1349,7 +1315,7 @@
             return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
         }
 
-        PackageStateInternal pkgSetting = pkgSettingFunction.apply(pkgName);
+        PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
         if (pkgSetting == null || pkgSetting.getPkg() == null) {
             throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
         }
@@ -1437,33 +1403,32 @@
     @Override
     public void clearDomainVerificationState(@Nullable List<String> packageNames) {
         mEnforcer.assertInternal(mConnection.getCallingUid());
-        mConnection.withPackageSettingsSnapshot(pkgSettings -> {
-            synchronized (mLock) {
-                if (packageNames == null) {
-                    int size = mAttachedPkgStates.size();
-                    for (int index = 0; index < size; index++) {
-                        DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
-                        String pkgName = pkgState.getPackageName();
-                        PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
-                        if (pkgSetting == null || pkgSetting.getPkg() == null) {
-                            continue;
-                        }
-                        resetDomainState(pkgState.getStateMap(), pkgSetting);
+        synchronized (mLock) {
+            final Computer snapshot = mConnection.snapshot();
+            if (packageNames == null) {
+                int size = mAttachedPkgStates.size();
+                for (int index = 0; index < size; index++) {
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                    String pkgName = pkgState.getPackageName();
+                    PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
                     }
-                } else {
-                    int size = packageNames.size();
-                    for (int index = 0; index < size; index++) {
-                        String pkgName = packageNames.get(index);
-                        DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
-                        PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
-                        if (pkgSetting == null || pkgSetting.getPkg() == null) {
-                            continue;
-                        }
-                        resetDomainState(pkgState.getStateMap(), pkgSetting);
+                    resetDomainState(pkgState.getStateMap(), pkgSetting);
+                }
+            } else {
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String pkgName = packageNames.get(index);
+                    DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
+                    PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(pkgName);
+                    if (pkgSetting == null || pkgSetting.getPkg() == null) {
+                        continue;
                     }
+                    resetDomainState(pkgState.getStateMap(), pkgSetting);
                 }
             }
-        });
+        }
 
         mConnection.scheduleWriteSettings();
     }
@@ -1935,35 +1900,32 @@
     @GuardedBy("mLock")
     @NonNull
     private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain,
-            @UserIdInt int userId, int minimumApproval,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+            @UserIdInt int userId, int minimumApproval, @NonNull Computer snapshot) {
         boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE;
         int highestApproval = minimumApproval;
         List<String> approvedPackages = emptyList();
 
-        synchronized (mLock) {
-            final int size = mAttachedPkgStates.size();
-            for (int index = 0; index < size; index++) {
-                DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
-                String packageName = pkgState.getPackageName();
-                PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
-                if (pkgSetting == null) {
-                    continue;
-                }
+        final int size = mAttachedPkgStates.size();
+        for (int index = 0; index < size; index++) {
+            DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+            String packageName = pkgState.getPackageName();
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
+            if (pkgSetting == null) {
+                continue;
+            }
 
-                int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
-                        domain);
-                if (level < minimumApproval) {
-                    continue;
-                }
+            int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
+                    domain);
+            if (level < minimumApproval) {
+                continue;
+            }
 
-                if (level > highestApproval) {
-                    approvedPackages.clear();
-                    approvedPackages = CollectionUtils.add(approvedPackages, packageName);
-                    highestApproval = level;
-                } else if (level == highestApproval) {
-                    approvedPackages = CollectionUtils.add(approvedPackages, packageName);
-                }
+            if (level > highestApproval) {
+                approvedPackages.clear();
+                approvedPackages = CollectionUtils.add(approvedPackages, packageName);
+                highestApproval = level;
+            } else if (level == highestApproval) {
+                approvedPackages = CollectionUtils.add(approvedPackages, packageName);
             }
         }
 
@@ -1976,7 +1938,7 @@
         final int approvedSize = approvedPackages.size();
         for (int index = 0; index < approvedSize; index++) {
             String packageName = approvedPackages.get(index);
-            PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
+            PackageStateInternal pkgSetting = snapshot.getPackageStateInternal(packageName);
             if (pkgSetting == null) {
                 continue;
             }
@@ -2035,108 +1997,4 @@
             return mErrorCode;
         }
     }
-
-    /**
-     * Wraps a {@link Connection} to verify that the {@link PackageStateInternal} calls do not hold
-     * {@link #mLock}, as that can cause deadlock when {@link Settings} tries to serialize state to
-     * disk. Only enabled if {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} is true.
-     */
-    private class LockSafeConnection implements Connection {
-
-        @NonNull
-        private final Connection mConnection;
-
-        private LockSafeConnection(@NonNull Connection connection) {
-            mConnection = connection;
-        }
-
-        private void enforceLocking() {
-            if (Thread.holdsLock(mLock)) {
-                Slog.wtf(TAG, "Method should not hold DVS lock when accessing package data");
-            }
-        }
-
-        @Override
-        public void withPackageSettingsSnapshot(
-                @NonNull Consumer<Function<String, PackageStateInternal>> block) {
-            enforceLocking();
-            mConnection.withPackageSettingsSnapshot(block);
-        }
-
-        @Override
-        public <Output> Output withPackageSettingsSnapshotReturning(
-                @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
-                        Output> block) {
-            enforceLocking();
-            return mConnection.withPackageSettingsSnapshotReturning(block);
-        }
-
-        @Override
-        public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
-                @NonNull FunctionalUtils.ThrowingCheckedConsumer<
-                        Function<String, PackageStateInternal>, ExceptionType> block)
-                throws ExceptionType {
-            enforceLocking();
-            mConnection.withPackageSettingsSnapshotThrowing(block);
-        }
-
-        @Override
-        public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
-                withPackageSettingsSnapshotThrowing2(
-                        @NonNull FunctionalUtils.ThrowingChecked2Consumer<
-                                Function<String, PackageStateInternal>, ExceptionOne,
-                                ExceptionTwo> block)
-                throws ExceptionOne, ExceptionTwo {
-            enforceLocking();
-            mConnection.withPackageSettingsSnapshotThrowing2(block);
-        }
-
-        @Override
-        public <Output, ExceptionType extends Exception> Output
-                withPackageSettingsSnapshotReturningThrowing(
-                        @NonNull FunctionalUtils.ThrowingCheckedFunction<
-                                Function<String, PackageStateInternal>, Output,
-                                ExceptionType> block)
-                throws ExceptionType {
-            enforceLocking();
-            return mConnection.withPackageSettingsSnapshotReturningThrowing(block);
-        }
-
-        @Override
-        public void scheduleWriteSettings() {
-            mConnection.scheduleWriteSettings();
-        }
-
-        @Override
-        public int getCallingUid() {
-            return mConnection.getCallingUid();
-        }
-
-        @Override
-        @UserIdInt
-        public int getCallingUserId() {
-            return mConnection.getCallingUserId();
-        }
-
-        @Override
-        public void schedule(int code, @Nullable Object object) {
-            mConnection.schedule(code, object);
-        }
-
-        @Override
-        @UserIdInt
-        public int[] getAllUserIds() {
-            return mConnection.getAllUserIds();
-        }
-
-        @Override
-        public boolean filterAppAccess(@NonNull String packageName, int callingUid, int userId) {
-            return mConnection.filterAppAccess(packageName, callingUid, userId);
-        }
-
-        @Override
-        public boolean doesUserExist(int userId) {
-            return mConnection.doesUserExist(userId);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index 015d4e9..8d1ae0b 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -28,6 +28,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.Computer;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
@@ -101,8 +102,7 @@
      */
     public void readSettings(@NonNull TypedXmlPullParser parser,
             @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
-            throws IOException, XmlPullParserException {
+            @NonNull Computer snapshot) throws IOException, XmlPullParserException {
         DomainVerificationPersistence.ReadResult result =
                 DomainVerificationPersistence.readFromXml(parser);
         ArrayMap<String, DomainVerificationPkgState> active = result.active;
@@ -118,7 +118,7 @@
                     // This branch should never be possible. Settings should be read from disk
                     // before any states are attached. But just in case, handle it.
                     if (!existingState.getId().equals(pkgState.getId())) {
-                        mergePkgState(existingState, pkgState, pkgSettingFunction);
+                        mergePkgState(existingState, pkgState, snapshot);
                     }
                 } else {
                     mPendingPkgStates.put(pkgName, pkgState);
@@ -139,8 +139,7 @@
      */
     public void restoreSettings(@NonNull TypedXmlPullParser parser,
             @NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
-            throws IOException, XmlPullParserException {
+            @NonNull Computer snapshot) throws IOException, XmlPullParserException {
         // TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on
         //  a new device.
 
@@ -166,7 +165,7 @@
                 }
 
                 if (existingState != null) {
-                    mergePkgState(existingState, newState, pkgSettingFunction);
+                    mergePkgState(existingState, newState, snapshot);
                 } else {
                     // If there's no existing state, that means the new state has to be transformed
                     // in preparation for attaching to brand new package that may eventually be
@@ -216,9 +215,9 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void mergePkgState(@NonNull DomainVerificationPkgState oldState,
-            @NonNull DomainVerificationPkgState newState,
-            @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
-        PackageStateInternal pkgSetting = pkgSettingFunction.apply(oldState.getPackageName());
+            @NonNull DomainVerificationPkgState newState, @NonNull Computer snapshot) {
+        PackageStateInternal pkgSetting =
+                snapshot.getPackageStateInternal(oldState.getPackageName());
         AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
         Set<String> validDomains = pkg == null
                 ? Collections.emptySet() : mCollector.collectValidAutoVerifyDomains(pkg);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index da2d162..4b0a8e2 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -32,6 +32,7 @@
 import android.util.IndentingPrintWriter;
 
 import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.server.pm.Computer;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -39,7 +40,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.function.Function;
 
 public class DomainVerificationShell {
 
@@ -599,8 +599,8 @@
         void verifyPackages(@Nullable List<String> packageNames, boolean reVerify);
 
         /**
-         * @see DomainVerificationManagerInternal#printState(IndentingPrintWriter, String, Integer,
-         * Function)
+         * @see DomainVerificationManagerInternal#printState(Computer, IndentingPrintWriter, String,
+         * Integer)
          */
         void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
                 @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 18c45e4..4ad6ed1 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -33,6 +33,7 @@
 import android.location.LocationManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PackageTagsList;
 import android.os.Process;
@@ -342,8 +343,11 @@
                     + Intent.ACTION_ACTIVITY_RECOGNIZER +  ", ignoring!");
             return;
         }
-        final String tagsList = resolvedService.serviceInfo.metaData.getString(
-                ACTIVITY_RECOGNITION_TAGS);
+        final Bundle metaData = resolvedService.serviceInfo.metaData;
+        if (metaData == null) {
+            return;
+        }
+        final String tagsList = metaData.getString(ACTIVITY_RECOGNITION_TAGS);
         if (!TextUtils.isEmpty(tagsList)) {
             PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
                     resolvedService.serviceInfo.packageName,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4b61970..4387249 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3547,6 +3547,10 @@
         if (mCameraLensCoverState == lensCoverState) {
             return;
         }
+        if (!mContext.getResources().getBoolean(
+                R.bool.config_launchCameraOnCameraLensCoverToggle)) {
+            return;
+        }
         if (mCameraLensCoverState == CAMERA_LENS_COVERED &&
                 lensCoverState == CAMERA_LENS_UNCOVERED) {
             Intent intent;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e523153..bd58472 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -128,6 +128,8 @@
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
 import com.android.server.power.batterysaver.BatterySavingStats;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -1175,7 +1177,10 @@
     @Override
     public void onBootPhase(int phase) {
         synchronized (mLock) {
-            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                systemReady();
+
+            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 incrementBootCount();
 
             } else if (phase == PHASE_BOOT_COMPLETED) {
@@ -1201,7 +1206,7 @@
         }
     }
 
-    public void systemReady() {
+    private void systemReady() {
         synchronized (mLock) {
             mSystemReady = true;
             mDreamManager = getLocalService(DreamManagerInternal.class);
@@ -4340,6 +4345,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private void dumpInternal(PrintWriter pw) {
         pw.println("POWER MANAGER (dumpsys power)\n");
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index ffcb2bd..b4613a7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -276,7 +276,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService#systemReady}, *with no lock held.*
+     * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
      */
     public void systemReady() {
         ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 1856786..fd64c75 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,16 +16,11 @@
 
 package com.android.server.rollback;
 
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
-
 import android.annotation.AnyThread;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
@@ -240,64 +235,6 @@
         return null;
     }
 
-    @WorkerThread
-    private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
-            int rollbackId, @Nullable VersionedPackage logPackage) {
-        assertInWorkerThread();
-        BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                handleStagedSessionChange(rollbackManager,
-                        rollbackId, this /* BroadcastReceiver */, logPackage);
-            }
-        };
-        IntentFilter sessionUpdatedFilter =
-                new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
-        mContext.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter, null, mHandler);
-        return sessionUpdatedReceiver;
-    }
-
-    @WorkerThread
-    private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
-            BroadcastReceiver listener, @Nullable VersionedPackage logPackage) {
-        assertInWorkerThread();
-        PackageInstaller packageInstaller =
-                mContext.getPackageManager().getPackageInstaller();
-        List<RollbackInfo> recentRollbacks =
-                rollbackManager.getRecentlyCommittedRollbacks();
-        for (int i = 0; i < recentRollbacks.size(); i++) {
-            RollbackInfo recentRollback = recentRollbacks.get(i);
-            int sessionId = recentRollback.getCommittedSessionId();
-            if ((rollbackId == recentRollback.getRollbackId())
-                    && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
-                PackageInstaller.SessionInfo sessionInfo =
-                        packageInstaller.getSessionInfo(sessionId);
-                if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
-                    mContext.unregisterReceiver(listener);
-                    saveStagedRollbackId(rollbackId, logPackage);
-                    WatchdogRollbackLogger.logEvent(logPackage,
-                            FrameworkStatsLog
-                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
-                            WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
-                            "");
-                } else if (sessionInfo.isStagedSessionFailed()
-                        && markStagedSessionHandled(rollbackId)) {
-                    WatchdogRollbackLogger.logEvent(logPackage,
-                            FrameworkStatsLog
-                                    .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                            WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
-                            "");
-                    mContext.unregisterReceiver(listener);
-                }
-            }
-        }
-
-        // Wait for all pending staged sessions to get handled before rebooting.
-        if (isPendingStagedSessionsEmpty()) {
-            mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
-        }
-    }
-
     /**
      * Returns {@code true} if staged session associated with {@code rollbackId} was marked
      * as handled, {@code false} if already handled.
@@ -447,12 +384,12 @@
             if (status == RollbackManager.STATUS_SUCCESS) {
                 if (rollback.isStaged()) {
                     int rollbackId = rollback.getRollbackId();
-                    mPendingStagedRollbackIds.add(rollbackId);
-                    BroadcastReceiver listener =
-                            listenForStagedSessionReady(rollbackManager, rollbackId,
-                                    logPackage);
-                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
-                            logPackage);
+                    saveStagedRollbackId(rollbackId, logPackage);
+                    WatchdogRollbackLogger.logEvent(logPackage,
+                            FrameworkStatsLog
+                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
+                            reasonToLog, failedPackageToLog);
+
                 } else {
                     WatchdogRollbackLogger.logEvent(logPackage,
                             FrameworkStatsLog
@@ -460,14 +397,18 @@
                             reasonToLog, failedPackageToLog);
                 }
             } else {
-                if (rollback.isStaged()) {
-                    markStagedSessionHandled(rollback.getRollbackId());
-                }
                 WatchdogRollbackLogger.logEvent(logPackage,
                         FrameworkStatsLog
                                 .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                         reasonToLog, failedPackageToLog);
             }
+            if (rollback.isStaged()) {
+                markStagedSessionHandled(rollback.getRollbackId());
+                // Wait for all pending staged sessions to get handled before rebooting.
+                if (isPendingStagedSessionsEmpty()) {
+                    mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
+                }
+            }
         };
 
         final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> {
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
index ce9fff5..e79efdb8 100644
--- a/services/core/java/com/android/server/sensorprivacy/PersistedState.java
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -295,6 +295,11 @@
                 TypeUserSensor userSensor = states.keyAt(i);
                 SensorState sensorState = states.valueAt(i);
 
+                // Do not persist hardware toggle states. Will be restored on reboot
+                if (userSensor.mType != SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
+                    continue;
+                }
+
                 serializer.startTag(null, XML_TAG_SENSOR_STATE);
                 serializer.attributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE,
                         userSensor.mType);
@@ -473,7 +478,7 @@
                 for (int j = 0; j < numSensors; j++) {
                     int sensor = userIndividualEnabled.keyAt(j);
                     SensorState sensorState = userIndividualEnabled.valueAt(j);
-                    result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+                    result.addState(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
                             userId, sensor, sensorState.getState(), sensorState.getLastChange());
                 }
             }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 19c8cd2..a8e2d43 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -39,6 +39,8 @@
 import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
 import static android.hardware.SensorPrivacyManager.Sources.SHELL;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 import static android.os.UserHandle.USER_NULL;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
@@ -108,7 +110,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Pair;
-import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -127,6 +128,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -139,7 +141,7 @@
     private static final boolean DEBUG_LOGGING = false;
 
     private static final String SENSOR_PRIVACY_CHANNEL_ID = Context.SENSOR_PRIVACY_SERVICE;
-    private static final String ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY =
+    private static final String ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY =
             SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy";
 
     public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
@@ -273,12 +275,12 @@
             mContext.registerReceiver(new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    setIndividualSensorPrivacy(
+                    setToggleSensorPrivacy(
                             ((UserHandle) intent.getParcelableExtra(
                                     Intent.EXTRA_USER)).getIdentifier(), OTHER,
                             intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
                 }
-            }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY),
+            }, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY),
                     MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
 
             mContext.registerReceiver(new BroadcastReceiver() {
@@ -299,7 +301,7 @@
             mSensorPrivacyStateController.setSensorPrivacyListener(
                     mHandler,
                     (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged(
-                            userId, sensor, state.isEnabled()));
+                            userId, toggleType, sensor, state.isEnabled()));
         }
 
         @Override
@@ -308,11 +310,12 @@
             // Reset sensor privacy when restriction is added
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) {
-                setIndividualSensorPrivacyUnchecked(userId, OTHER, CAMERA, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, CAMERA, false);
             }
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) {
-                setIndividualSensorPrivacyUnchecked(userId, OTHER, MICROPHONE, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, MICROPHONE,
+                        false);
             }
         }
 
@@ -352,15 +355,15 @@
         }
 
         /**
-         * Called when a sensor protected by individual sensor privacy is attempting to get used.
+         * Called when a sensor protected by toggle sensor privacy is attempting to get used.
          *
          * @param uid The uid of the app using the sensor
          * @param packageName The package name of the app using the sensor
          * @param sensor The sensor that is attempting to be used
          */
         private void onSensorUseStarted(int uid, String packageName, int sensor) {
-            UserHandle user = UserHandle.getUserHandleForUid(uid);
-            if (!isIndividualSensorPrivacyEnabled(user.getIdentifier(), sensor)) {
+            UserHandle user = UserHandle.of(mCurrentUser);
+            if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
                 return;
             }
 
@@ -371,12 +374,10 @@
             }
 
             synchronized (mLock) {
-                UserHandle parentUser = UserHandle.of(mUserManagerInternal
-                        .getProfileParentId(user.getIdentifier()));
-                if (mSuppressReminders.containsKey(new Pair<>(sensor, parentUser))) {
+                if (mSuppressReminders.containsKey(new Pair<>(sensor, user))) {
                     Log.d(TAG,
                             "Suppressed sensor privacy reminder for " + packageName + "/"
-                                    + parentUser);
+                                    + user);
                     return;
                 }
             }
@@ -406,8 +407,8 @@
                         }
 
                         tasksOfPackageUsingSensor.add(task);
-                    } else if (task.topActivity.flattenToString().equals(mContext.getResources()
-                            .getString(R.string.config_sensorUseStartedActivity))
+                    } else if (task.topActivity.flattenToString().equals(
+                            getSensorUseActivityName(new ArraySet<>(Arrays.asList(sensor))))
                             && task.isFocused) {
                         enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName,
                                 sensor);
@@ -442,7 +443,7 @@
 
             String inputMethodComponent = Settings.Secure.getStringForUser(
                     mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
-                    mCurrentUser);
+                    user.getIdentifier());
             String inputMethodPackageName = null;
             if (inputMethodComponent != null) {
                 inputMethodPackageName = ComponentName.unflattenFromString(
@@ -534,9 +535,8 @@
                 return;
             }
             Intent dialogIntent = new Intent();
-            dialogIntent.setComponent(ComponentName.unflattenFromString(
-                    mContext.getResources().getString(
-                            R.string.config_sensorUseStartedActivity)));
+            dialogIntent.setComponent(
+                    ComponentName.unflattenFromString(getSensorUseActivityName(sensors)));
 
             ActivityOptions options = ActivityOptions.makeBasic();
             options.setLaunchTaskId(info.mTaskId);
@@ -560,6 +560,21 @@
         }
 
         /**
+         * Get the activity component based on which privacy toggles are enabled.
+         * @param sensors
+         * @return component name to launch
+         */
+        private String getSensorUseActivityName(ArraySet<Integer> sensors) {
+            for (Integer sensor : sensors) {
+                if (isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor)) {
+                    return mContext.getResources().getString(
+                            R.string.config_sensorUseStartedActivity_hwToggle);
+                }
+            }
+            return mContext.getResources().getString(R.string.config_sensorUseStartedActivity);
+        }
+
+        /**
          * Show a notification that informs the user that a sensor use or a blocked sensor started.
          * The user can then react to this event.
          *
@@ -619,7 +634,7 @@
             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)
+                    new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY)
                             .setPackage(mContext.getPackageName())
                             .putExtra(EXTRA_SENSOR, sensor)
                             .putExtra(Intent.EXTRA_USER, user),
@@ -657,12 +672,12 @@
         }
 
         @Override
-        public void setIndividualSensorPrivacy(@UserIdInt int userId,
+        public void setToggleSensorPrivacy(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             if (DEBUG) {
                 Log.d(TAG, "callingUid=" + Binder.getCallingUid()
                         + " callingPid=" + Binder.getCallingPid()
-                        + " setIndividualSensorPrivacy("
+                        + " setToggleSensorPrivacy("
                         + "userId=" + userId
                         + " source=" + source
                         + " sensor=" + sensor
@@ -673,22 +688,22 @@
             if (userId == UserHandle.USER_CURRENT) {
                 userId = mCurrentUser;
             }
-            if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
+            if (!canChangeToggleSensorPrivacy(userId, sensor)) {
                 return;
             }
 
-            setIndividualSensorPrivacyUnchecked(userId, source, sensor, enable);
+            setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable);
         }
 
-        private void setIndividualSensorPrivacyUnchecked(int userId, int source, int sensor,
-                boolean enable) {
+        private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
+                int sensor, boolean enable) {
             final long[] lastChange = new long[1];
             mSensorPrivacyStateController.atomic(() -> {
                 SensorState sensorState = mSensorPrivacyStateController
-                        .getState(SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor);
+                        .getState(toggleType, userId, sensor);
                 lastChange[0] = sensorState.getLastChange();
                 mSensorPrivacyStateController.setState(
-                        SensorPrivacyManager.ToggleTypes.SOFTWARE, userId, sensor, enable, mHandler,
+                        toggleType, userId, sensor, enable, mHandler,
                         changeSuccessful -> {
                             if (changeSuccessful) {
                                 if (userId == mUserManagerInternal.getProfileParentId(userId)) {
@@ -701,7 +716,7 @@
             });
         }
 
-        private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
+        private boolean canChangeToggleSensorPrivacy(@UserIdInt int userId, int sensor) {
             if (sensor == MICROPHONE && mCallStateHelper.isInEmergencyCall()) {
                 // During emergency call the microphone toggle managed automatically
                 Log.i(TAG, "Can't change mic toggle during an emergency call");
@@ -784,7 +799,7 @@
         }
 
         @Override
-        public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId,
+        public void setToggleSensorPrivacyForProfileGroup(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
             if (userId == UserHandle.USER_CURRENT) {
@@ -793,7 +808,7 @@
             int parentId = mUserManagerInternal.getProfileParentId(userId);
             forAllUsers(userId2 -> {
                 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
-                    setIndividualSensorPrivacy(userId2, source, sensor, enable);
+                    setToggleSensorPrivacy(userId2, source, sensor, enable);
                 }
             });
         }
@@ -835,31 +850,55 @@
         }
 
         @Override
-        public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+        public boolean isToggleSensorPrivacyEnabled(int toggleType, int sensor) {
             if (DEBUG) {
                 Log.d(TAG, "callingUid=" + Binder.getCallingUid()
                         + " callingPid=" + Binder.getCallingPid()
-                        + " isIndividualSensorPrivacyEnabled("
-                        + "userId=" + userId
+                        + " isToggleSensorPrivacyEnabled("
+                        + "toggleType=" + toggleType
                         + " sensor=" + sensor
                         + ")");
             }
             enforceObserveSensorPrivacyPermission();
-            if (userId == UserHandle.USER_CURRENT) {
-                userId = mCurrentUser;
-            }
-            return mSensorPrivacyStateController.getState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+
+            return mSensorPrivacyStateController.getState(toggleType, mCurrentUser, sensor)
+                    .isEnabled();
+        }
+
+        @Override
+        public boolean isCombinedToggleSensorPrivacyEnabled(int sensor) {
+            return isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
+                    || isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor);
+        }
+
+        private boolean isToggleSensorPrivacyEnabledInternal(int userId, int toggleType,
+                int sensor) {
+
+            return mSensorPrivacyStateController.getState(toggleType,
                     userId, sensor).isEnabled();
         }
 
         @Override
-        public boolean supportsSensorToggle(int sensor) {
-            if (sensor == MICROPHONE) {
-                return mContext.getResources().getBoolean(R.bool.config_supportsMicToggle);
-            } else if (sensor == CAMERA) {
-                return mContext.getResources().getBoolean(R.bool.config_supportsCamToggle);
+        public boolean supportsSensorToggle(int toggleType, int sensor) {
+            if (toggleType == TOGGLE_TYPE_SOFTWARE) {
+                if (sensor == MICROPHONE) {
+                    return mContext.getResources()
+                            .getBoolean(R.bool.config_supportsMicToggle);
+                } else if (sensor == CAMERA) {
+                    return mContext.getResources()
+                            .getBoolean(R.bool.config_supportsCamToggle);
+                }
+            } else if (toggleType == TOGGLE_TYPE_HARDWARE) {
+                if (sensor == MICROPHONE) {
+                    return mContext.getResources()
+                            .getBoolean(R.bool.config_supportsHardwareMicToggle);
+                } else if (sensor == CAMERA) {
+                    return mContext.getResources()
+                            .getBoolean(R.bool.config_supportsHardwareCamToggle);
+                }
             }
-            throw new IllegalArgumentException("Unable to find value " + sensor);
+            throw new IllegalArgumentException("Invalid arguments. "
+                    + "toggleType=" + toggleType + " sensor=" + sensor);
         }
 
         /**
@@ -878,29 +917,13 @@
          * Registers a listener to be notified when the sensor privacy state changes.
          */
         @Override
-        public void addIndividualSensorPrivacyListener(int userId, int sensor,
-                ISensorPrivacyListener listener) {
+        public void addToggleSensorPrivacyListener(ISensorPrivacyListener listener) {
+            Log.d("evan", "trying to add from " + Binder.getCallingUid());
             enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new IllegalArgumentException("listener cannot be null");
             }
-            mHandler.addListener(userId, sensor, listener);
-        }
-
-
-        /**
-         * Registers a listener to be notified when the sensor privacy state changes. The callback
-         * can be called if the user changes and the setting is different between the transitioning
-         * users.
-         */
-        @Override
-        public void addUserGlobalIndividualSensorPrivacyListener(int sensor,
-                ISensorPrivacyListener listener) {
-            enforceObserveSensorPrivacyPermission();
-            if (listener == null) {
-                throw new IllegalArgumentException("listener cannot be null");
-            }
-            mHandler.addUserGlobalListener(sensor, listener);
+            mHandler.addToggleListener(listener);
         }
 
         /**
@@ -919,27 +942,16 @@
          * Unregisters a listener from sensor privacy state change notifications.
          */
         @Override
-        public void removeIndividualSensorPrivacyListener(int sensor,
-                ISensorPrivacyListener listener) {
+        public void removeToggleSensorPrivacyListener(ISensorPrivacyListener listener) {
             enforceObserveSensorPrivacyPermission();
             if (listener == null) {
                 throw new IllegalArgumentException("listener cannot be null");
             }
-            mHandler.removeListener(sensor, listener);
+            mHandler.removeToggleListener(listener);
         }
 
         @Override
-        public void removeUserGlobalIndividualSensorPrivacyListener(int sensor,
-                ISensorPrivacyListener listener) {
-            enforceObserveSensorPrivacyPermission();
-            if (listener == null) {
-                throw new IllegalArgumentException("listener cannot be null");
-            }
-            mHandler.removeUserGlobalListener(sensor, listener);
-        }
-
-        @Override
-        public void suppressIndividualSensorPrivacyReminders(int userId, int sensor,
+        public void suppressToggleSensorPrivacyReminders(int userId, int sensor,
                 IBinder token, boolean suppress) {
             enforceManageSensorPrivacyPermission();
             if (userId == UserHandle.USER_CURRENT) {
@@ -976,7 +988,7 @@
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Can only be called by the system uid");
             }
-            if (!isIndividualSensorPrivacyEnabled(mCurrentUser, sensor)) {
+            if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
                 return;
             }
             enqueueSensorUseReminderDialogAsync(
@@ -984,23 +996,50 @@
         }
 
         private void userSwitching(int from, int to) {
-            final boolean[] micState = new boolean[1];
-            final boolean[] camState = new boolean[1];
-            final boolean[] prevMicState = new boolean[1];
-            final boolean[] prevCamState = new boolean[1];
+            final boolean[] micState = new boolean[2];
+            final boolean[] camState = new boolean[2];
+            final boolean[] prevMicState = new boolean[2];
+            final boolean[] prevCamState = new boolean[2];
+            final int swToggleIdx = 0;
+            final int hwToggleIdx = 1;
+            // Get SW toggles state
             mSensorPrivacyStateController.atomic(() -> {
-                prevMicState[0] = isIndividualSensorPrivacyEnabled(from, MICROPHONE);
-                prevCamState[0] = isIndividualSensorPrivacyEnabled(from, CAMERA);
-                micState[0] = isIndividualSensorPrivacyEnabled(to, MICROPHONE);
-                camState[0] = isIndividualSensorPrivacyEnabled(to, CAMERA);
+                prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
+                micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
             });
-            if (from == USER_NULL || prevMicState[0] != micState[0]) {
-                mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState[0]);
-                setGlobalRestriction(MICROPHONE, micState[0]);
+            // Get HW toggles state
+            mSensorPrivacyStateController.atomic(() -> {
+                prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
+                micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
+            });
+
+            if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx]
+                    || prevMicState[hwToggleIdx] != micState[hwToggleIdx]) {
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, MICROPHONE,
+                        micState[swToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, MICROPHONE,
+                        micState[hwToggleIdx]);
+                setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]);
             }
-            if (from == USER_NULL || prevCamState[0] != camState[0]) {
-                mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState[0]);
-                setGlobalRestriction(CAMERA, camState[0]);
+            if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx]
+                    || prevCamState[hwToggleIdx] != camState[hwToggleIdx]) {
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, CAMERA,
+                        camState[swToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, CAMERA,
+                        camState[hwToggleIdx]);
+                setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]);
             }
         }
 
@@ -1153,7 +1192,7 @@
                                 return -1;
                             }
 
-                            setIndividualSensorPrivacy(userId, SHELL, sensor, true);
+                            setToggleSensorPrivacy(userId, SHELL, sensor, true);
                         }
                         break;
                         case "disable" : {
@@ -1163,7 +1202,7 @@
                                 return -1;
                             }
 
-                            setIndividualSensorPrivacy(userId, SHELL, sensor, false);
+                            setToggleSensorPrivacy(userId, SHELL, sensor, false);
                         }
                         break;
                         default:
@@ -1205,11 +1244,8 @@
         private final RemoteCallbackList<ISensorPrivacyListener> mListeners =
                 new RemoteCallbackList<>();
         @GuardedBy("mListenerLock")
-        private final SparseArray<SparseArray<RemoteCallbackList<ISensorPrivacyListener>>>
-                mIndividualSensorListeners = new SparseArray<>();
-        @GuardedBy("mListenerLock")
-        private final SparseArray<RemoteCallbackList<ISensorPrivacyListener>>
-                mUserGlobalIndividualSensorListeners = new SparseArray<>();
+        private final RemoteCallbackList<ISensorPrivacyListener>
+                mToggleSensorListeners = new RemoteCallbackList<>();
         @GuardedBy("mListenerLock")
         private final ArrayMap<ISensorPrivacyListener, Pair<DeathRecipient, Integer>>
                 mDeathRecipients;
@@ -1221,12 +1257,6 @@
             mContext = context;
         }
 
-        public void onUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) {
-            sendMessage(PooledLambda.obtainMessage(
-                    SensorPrivacyHandler::handleUserGlobalSensorPrivacyChanged,
-                    this, sensor, enabled));
-        }
-
         public void addListener(ISensorPrivacyListener listener) {
             synchronized (mListenerLock) {
                 if (mListeners.register(listener)) {
@@ -1235,34 +1265,9 @@
             }
         }
 
-        public void addListener(int userId, int sensor, ISensorPrivacyListener listener) {
+        public void addToggleListener(ISensorPrivacyListener listener) {
             synchronized (mListenerLock) {
-                SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
-                        mIndividualSensorListeners.get(userId);
-                if (listenersForUser == null) {
-                    listenersForUser = new SparseArray<>();
-                    mIndividualSensorListeners.put(userId, listenersForUser);
-                }
-                RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
-                if (listeners == null) {
-                    listeners = new RemoteCallbackList<>();
-                    listenersForUser.put(sensor, listeners);
-                }
-                if (listeners.register(listener)) {
-                    addDeathRecipient(listener);
-                }
-            }
-        }
-
-        public void addUserGlobalListener(int sensor, ISensorPrivacyListener listener) {
-            synchronized (mListenerLock) {
-                RemoteCallbackList<ISensorPrivacyListener> listeners =
-                        mUserGlobalIndividualSensorListeners.get(sensor);
-                if (listeners == null) {
-                    listeners = new RemoteCallbackList<>();
-                    mUserGlobalIndividualSensorListeners.put(sensor, listeners);
-                }
-                if (listeners.register(listener)) {
+                if (mToggleSensorListeners.register(listener)) {
                     addDeathRecipient(listener);
                 }
             }
@@ -1276,28 +1281,10 @@
             }
         }
 
-        public void removeListener(int sensor, ISensorPrivacyListener listener) {
+        public void removeToggleListener(ISensorPrivacyListener listener) {
             synchronized (mListenerLock) {
-                for (int i = 0, numUsers = mIndividualSensorListeners.size(); i < numUsers; i++) {
-                    RemoteCallbackList callbacks =
-                            mIndividualSensorListeners.valueAt(i).get(sensor);
-                    if (callbacks != null) {
-                        if (callbacks.unregister(listener)) {
-                            removeDeathRecipient(listener);
-                        }
-                    }
-                }
-            }
-        }
-
-        public void removeUserGlobalListener(int sensor, ISensorPrivacyListener listener) {
-            synchronized (mListenerLock) {
-                RemoteCallbackList callbacks =
-                        mUserGlobalIndividualSensorListeners.get(sensor);
-                if (callbacks != null) {
-                    if (callbacks.unregister(listener)) {
-                        removeDeathRecipient(listener);
-                    }
+                if (mToggleSensorListeners.unregister(listener)) {
+                    removeDeathRecipient(listener);
                 }
             }
         }
@@ -1307,7 +1294,7 @@
             for (int i = 0; i < count; i++) {
                 ISensorPrivacyListener listener = mListeners.getBroadcastItem(i);
                 try {
-                    listener.onSensorPrivacyChanged(enabled);
+                    listener.onSensorPrivacyChanged(-1, -1, enabled);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
                 }
@@ -1315,62 +1302,34 @@
             mListeners.finishBroadcast();
         }
 
-        public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
-            // TODO handle hardware
+        public void handleSensorPrivacyChanged(int userId, int toggleType, int sensor,
+                boolean enabled) {
             mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
-            SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
-                    mIndividualSensorListeners.get(userId);
 
             if (userId == mCurrentUser) {
-                mSensorPrivacyServiceImpl.setGlobalRestriction(sensor, enabled);
+                mSensorPrivacyServiceImpl.setGlobalRestriction(sensor,
+                        mSensorPrivacyServiceImpl.isCombinedToggleSensorPrivacyEnabled(sensor));
             }
 
-            if (userId == mCurrentUser) {
-                onUserGlobalSensorPrivacyChanged(sensor, enabled);
-            }
-
-            if (listenersForUser == null) {
+            if (userId != mCurrentUser) {
                 return;
             }
-            RemoteCallbackList<ISensorPrivacyListener> listeners = listenersForUser.get(sensor);
-            if (listeners == null) {
-                return;
-            }
-            try {
-                final int count = listeners.beginBroadcast();
-                for (int i = 0; i < count; i++) {
-                    ISensorPrivacyListener listener = listeners.getBroadcastItem(i);
-                    try {
-                        listener.onSensorPrivacyChanged(enabled);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
+            synchronized (mListenerLock) {
+                try {
+                    final int count = mToggleSensorListeners.beginBroadcast();
+                    for (int i = 0; i < count; i++) {
+                        ISensorPrivacyListener listener = mToggleSensorListeners.getBroadcastItem(
+                                i);
+                        try {
+                            listener.onSensorPrivacyChanged(toggleType, sensor, enabled);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Caught an exception notifying listener " + listener + ": ",
+                                    e);
+                        }
                     }
+                } finally {
+                    mToggleSensorListeners.finishBroadcast();
                 }
-            } finally {
-                listeners.finishBroadcast();
-            }
-        }
-
-        public void handleUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) {
-            RemoteCallbackList<ISensorPrivacyListener> listeners =
-                    mUserGlobalIndividualSensorListeners.get(sensor);
-
-            if (listeners == null) {
-                return;
-            }
-
-            try {
-                final int count = listeners.beginBroadcast();
-                for (int i = 0; i < count; i++) {
-                    ISensorPrivacyListener listener = listeners.getBroadcastItem(i);
-                    try {
-                        listener.onSensorPrivacyChanged(enabled);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e);
-                    }
-                }
-            } finally {
-                listeners.finishBroadcast();
             }
         }
 
@@ -1482,7 +1441,8 @@
         @Override
         public boolean isSensorPrivacyEnabled(int userId, int sensor) {
             return SensorPrivacyService.this
-                    .mSensorPrivacyServiceImpl.isIndividualSensorPrivacyEnabled(userId, sensor);
+                    .mSensorPrivacyServiceImpl.isToggleSensorPrivacyEnabledInternal(userId,
+                            TOGGLE_TYPE_SOFTWARE, sensor);
         }
 
         @Override
@@ -1521,6 +1481,25 @@
                 sensorListeners.add(listener);
             }
         }
+
+        @Override
+        public void setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable) {
+            final SensorPrivacyServiceImpl sps =
+                    SensorPrivacyService.this.mSensorPrivacyServiceImpl;
+
+            // Convert userId to actual user Id. mCurrentUser is USER_NULL if toggle state is set
+            // before onUserStarting.
+            userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId);
+            final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId);
+
+            sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_HARDWARE, realUserId, OTHER, sensor,
+                    enable);
+            // Also disable the SW toggle when disabling the HW toggle
+            if (!enable) {
+                sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, realUserId, OTHER, sensor,
+                        enable);
+            }
+        }
     }
 
     private class CallStateHelper {
@@ -1574,9 +1553,9 @@
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
-                            .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE)) {
-                        mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                mCurrentUser, OTHER, MICROPHONE, false);
+                            .isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, MICROPHONE)) {
+                        mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
                         mMicUnmutedForEmergencyCall = true;
                     } else {
                         mMicUnmutedForEmergencyCall = false;
@@ -1601,8 +1580,8 @@
                 if (mIsInEmergencyCall) {
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
-                        mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                mCurrentUser, OTHER, MICROPHONE, true);
+                        mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
                         mMicUnmutedForEmergencyCall = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
index d1ea8e9..3dcb4cf 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.server.sensorprivacy;
 
-import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
 
 import com.android.internal.util.dump.DualDumpOutputStream;
@@ -49,10 +48,6 @@
 
     @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);
@@ -67,12 +62,6 @@
     @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);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index c638201..9bbae4b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -29,7 +29,9 @@
 import android.os.IHwBinder;
 import android.os.RemoteException;
 import android.system.OsConstants;
+import android.util.Log;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -54,6 +56,8 @@
  * </ul>
  */
 final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+    private static final String TAG = "SoundTriggerHw2Compat";
+
     private final @NonNull Runnable mRebootRunnable;
     private final @NonNull IHwBinder mBinder;
     private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.data != null) {
+                try {
+                    hidlModel.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
@@ -252,6 +266,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.common.data != null) {
+                try {
+                    hidlModel.common.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index adee325..1b15351 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -15,7 +15,6 @@
  */
 
 package com.android.server.stats.pull;
-
 import static android.app.AppOpsManager.OP_FLAG_SELF;
 import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
@@ -74,6 +73,7 @@
 import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManager.HistoricalUidOps;
 import android.app.INotificationManager;
+import android.app.PendingIntentStats;
 import android.app.ProcessMemoryState;
 import android.app.RuntimeAppOpAccessMessage;
 import android.app.StatsManager;
@@ -197,6 +197,8 @@
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+import com.android.server.PinnerService.PinnedFileStats;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
@@ -727,6 +729,10 @@
                         return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
                     case FrameworkStatsLog.MEDIA_CAPABILITIES:
                         return pullMediaCapabilitiesStats(atomTag, data);
+                    case FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE:
+                        return pullSystemServerPinnerStats(atomTag, data);
+                    case FrameworkStatsLog.PENDING_INTENTS_PER_PACKAGE:
+                        return pullPendingIntentsPerPackage(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -923,6 +929,8 @@
         registerAccessibilityShortcutStats();
         registerAccessibilityFloatingMenuStats();
         registerMediaCapabilitiesStats();
+        registerPendingIntentsPerPackagePuller();
+        registerPinnerServiceStats();
     }
 
     private void initAndRegisterNetworkStatsPullers() {
@@ -1072,7 +1080,7 @@
     private void registerWifiBytesTransfer() {
         int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3, 4, 5})
+                .setAdditiveFields(new int[]{2, 3, 4, 5})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1085,12 +1093,12 @@
     @NonNull
     private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtom(int atomTag) {
         List<NetworkStatsExt> ret = new ArrayList<>();
-        switch(atomTag) {
+        switch (atomTag) {
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
-                            new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
+                            new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
                 }
                 break;
             }
@@ -1098,7 +1106,7 @@
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                                    new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
+                            new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
                 }
                 break;
             }
@@ -1107,7 +1115,7 @@
                         getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
-                                    new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
+                            new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
                 }
                 break;
             }
@@ -1116,7 +1124,7 @@
                         getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                                    new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
+                            new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
                 }
                 break;
             }
@@ -1125,11 +1133,11 @@
                         new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
                 final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
                         new NetworkTemplate.Builder(MATCH_MOBILE)
-                        .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
+                                .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
                 if (wifiStats != null && cellularStats != null) {
                     final NetworkStats stats = wifiStats.add(cellularStats);
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
-                            new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR},
+                            new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR},
                             /*slicedByFgbg=*/false, /*slicedByTag=*/true,
                             /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
                             /*subInfo=*/null, OEM_MANAGED_ALL));
@@ -1196,7 +1204,8 @@
         return StatsManager.PULL_SUCCESS;
     }
 
-    @NonNull private static NetworkStats removeEmptyEntries(NetworkStats stats) {
+    @NonNull
+    private static NetworkStats removeEmptyEntries(NetworkStats stats) {
         NetworkStats ret = new NetworkStats(0, 1);
         for (NetworkStats.Entry e : stats) {
             if (e.getRxBytes() != 0 || e.getRxPackets() != 0 || e.getTxBytes() != 0
@@ -1278,13 +1287,14 @@
         }
     }
 
-    @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
+    @NonNull
+    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
         final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of(
                 new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET),
                 new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR),
                 new Pair(MATCH_WIFI, TRANSPORT_WIFI)
         );
-        final int[] oemManagedTypes = new int[] {OEM_MANAGED_PAID | OEM_MANAGED_PRIVATE,
+        final int[] oemManagedTypes = new int[]{OEM_MANAGED_PAID | OEM_MANAGED_PRIVATE,
                 OEM_MANAGED_PAID, OEM_MANAGED_PRIVATE};
 
         final List<NetworkStatsExt> ret = new ArrayList<>();
@@ -1301,7 +1311,7 @@
                 final Integer transport = ruleAndTransport.second;
                 if (stats != null) {
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                            new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+                            new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
                             /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
                             /*subInfo=*/null, oemManaged));
                 }
@@ -1314,7 +1324,8 @@
     /**
      * Create a snapshot of NetworkStats for a given transport.
      */
-    @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
+    @Nullable
+    private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
         NetworkTemplate template = null;
         switch (transport) {
             case TRANSPORT_CELLULAR:
@@ -1336,7 +1347,8 @@
      * Note that this should be only used to calculate diff since the snapshot might contains
      * some traffic before boot.
      */
-    @Nullable private NetworkStats getUidNetworkStatsSnapshotForTemplate(
+    @Nullable
+    private NetworkStats getUidNetworkStatsSnapshotForTemplate(
             @NonNull NetworkTemplate template, boolean includeTags) {
         final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
         final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
@@ -1354,8 +1366,8 @@
 
         final android.app.usage.NetworkStats queryNonTaggedStats =
                 mNetworkStatsManager.querySummary(
-                template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
-                currentTimeInMillis);
+                        template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+                        currentTimeInMillis);
 
         final NetworkStats nonTaggedStats =
                 NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
@@ -1363,27 +1375,28 @@
 
         final android.app.usage.NetworkStats queryTaggedStats =
                 mNetworkStatsManager.queryTaggedSummary(template,
-                currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
-                currentTimeInMillis);
+                        currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
+                        currentTimeInMillis);
         final NetworkStats taggedStats =
                 NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats);
         return nonTaggedStats.add(taggedStats);
     }
 
-    @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
+    @NonNull
+    private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
             @NonNull SubInfo subInfo) {
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
             final NetworkTemplate template =
                     new NetworkTemplate.Builder(MATCH_MOBILE)
-                    .setSubscriberIds(Set.of(subInfo.subscriberId))
-                    .setRatType(ratType)
-                    .setMeteredness(METERED_YES).build();
+                            .setSubscriberIds(Set.of(subInfo.subscriberId))
+                            .setRatType(ratType)
+                            .setMeteredness(METERED_YES).build();
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
             if (stats != null) {
                 ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
-                        new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
+                        new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
                         /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo,
                         OEM_MANAGED_ALL));
             }
@@ -1412,51 +1425,55 @@
         return com.android.net.module.util.CollectionUtils.toIntArray(collapsedRatTypes);
     }
 
-    @NonNull private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) {
+    @NonNull
+    private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
                 (entry) -> {
-                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
-                                NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
-                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
-                                NetworkStats.DEFAULT_NETWORK_ALL,
-                                entry.getRxBytes(), entry.getRxPackets(),
-                                entry.getTxBytes(), entry.getTxPackets(), 0);
+                    return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                            NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
+                            NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                            NetworkStats.DEFAULT_NETWORK_ALL,
+                            entry.getRxBytes(), entry.getRxPackets(),
+                            entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
-    @NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
+    @NonNull
+    private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
                 (entry) -> {
-                        return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
-                                entry.getSet(), NetworkStats.TAG_NONE,
-                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
-                                NetworkStats.DEFAULT_NETWORK_ALL,
-                                entry.getRxBytes(), entry.getRxPackets(),
-                                entry.getTxBytes(), entry.getTxPackets(), 0);
+                    return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
+                            entry.getSet(), NetworkStats.TAG_NONE,
+                            NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                            NetworkStats.DEFAULT_NETWORK_ALL,
+                            entry.getRxBytes(), entry.getRxPackets(),
+                            entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
-    @NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
+    @NonNull
+    private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
                 (entry) -> {
-                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
-                                entry.getSet(), NetworkStats.TAG_NONE,
-                                NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
-                                NetworkStats.DEFAULT_NETWORK_ALL,
-                                entry.getRxBytes(), entry.getRxPackets(),
-                                entry.getTxBytes(), entry.getTxPackets(), 0);
+                    return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                            entry.getSet(), NetworkStats.TAG_NONE,
+                            NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                            NetworkStats.DEFAULT_NETWORK_ALL,
+                            entry.getRxBytes(), entry.getRxPackets(),
+                            entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
-    @NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
+    @NonNull
+    private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
         return sliceNetworkStats(stats,
                 (entry) -> {
-                        return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
-                                NetworkStats.SET_ALL, entry.getTag(),
-                                entry.getMetered(), NetworkStats.ROAMING_ALL,
-                                NetworkStats.DEFAULT_NETWORK_ALL,
-                                entry.getRxBytes(), entry.getRxPackets(),
-                                entry.getTxBytes(), entry.getTxPackets(), 0);
+                    return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(),
+                            NetworkStats.SET_ALL, entry.getTag(),
+                            entry.getMetered(), NetworkStats.ROAMING_ALL,
+                            NetworkStats.DEFAULT_NETWORK_ALL,
+                            entry.getRxBytes(), entry.getRxPackets(),
+                            entry.getTxBytes(), entry.getTxPackets(), 0);
                 });
     }
 
@@ -1472,17 +1489,18 @@
      *               get the state from entry to replace the default value.
      *               This is useful for slicing by particular dimensions. For example, if we wished
      *               to slice by uid and tag, we could write the following lambda:
-     *                  (entry) -> {
-     *                   return new NetworkStats.Entry(null, entry.getUid(),
-     *                           NetworkStats.SET_ALL, entry.getTag(),
-     *                           NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
-     *                           NetworkStats.DEFAULT_NETWORK_ALL,
-     *                           entry.getRxBytes(), entry.getRxPackets(),
-     *                           entry.getTxBytes(), entry.getTxPackets(), 0);
-     *                  }
+     *               (entry) -> {
+     *               return new NetworkStats.Entry(null, entry.getUid(),
+     *               NetworkStats.SET_ALL, entry.getTag(),
+     *               NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+     *               NetworkStats.DEFAULT_NETWORK_ALL,
+     *               entry.getRxBytes(), entry.getRxPackets(),
+     *               entry.getTxBytes(), entry.getTxPackets(), 0);
+     *               }
      * @return new NeworkStats object appropriately sliced
      */
-    @NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
+    @NonNull
+    private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
             @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) {
         NetworkStats ret = new NetworkStats(0, 1);
         for (NetworkStats.Entry e : stats) {
@@ -1494,7 +1512,7 @@
     private void registerWifiBytesTransferBackground() {
         int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {3, 4, 5, 6})
+                .setAdditiveFields(new int[]{3, 4, 5, 6})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1507,7 +1525,7 @@
     private void registerMobileBytesTransfer() {
         int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3, 4, 5})
+                .setAdditiveFields(new int[]{2, 3, 4, 5})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1520,7 +1538,7 @@
     private void registerMobileBytesTransferBackground() {
         int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {3, 4, 5, 6})
+                .setAdditiveFields(new int[]{3, 4, 5, 6})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1533,7 +1551,7 @@
     private void registerBytesTransferByTagAndMetered() {
         int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {4, 5, 6, 7})
+                .setAdditiveFields(new int[]{4, 5, 6, 7})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1546,7 +1564,7 @@
     private void registerDataUsageBytesTransfer() {
         int tagId = FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3, 4, 5})
+                .setAdditiveFields(new int[]{2, 3, 4, 5})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1559,7 +1577,7 @@
     private void registerOemManagedBytesTransfer() {
         int tagId = FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {5, 6, 7, 8})
+                .setAdditiveFields(new int[]{5, 6, 7, 8})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1572,7 +1590,7 @@
     private void registerBluetoothBytesTransfer() {
         int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3})
+                .setAdditiveFields(new int[]{2, 3})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1661,7 +1679,7 @@
         if (KernelCpuBpfTracking.isSupported()) {
             int tagId = FrameworkStatsLog.CPU_TIME_PER_CLUSTER_FREQ;
             PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                    .setAdditiveFields(new int[] {3})
+                    .setAdditiveFields(new int[]{3})
                     .build();
             mStatsManager.setPullAtomCallback(
                     tagId,
@@ -1691,7 +1709,7 @@
     private void registerCpuTimePerUid() {
         int tagId = FrameworkStatsLog.CPU_TIME_PER_UID;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3})
+                .setAdditiveFields(new int[]{2, 3})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1716,7 +1734,7 @@
         if (KernelCpuBpfTracking.isSupported() || KernelCpuBpfTracking.getClusters() > 0) {
             int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER;
             PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                    .setAdditiveFields(new int[] {3, 4, 5})
+                    .setAdditiveFields(new int[]{3, 4, 5})
                     .build();
             mStatsManager.setPullAtomCallback(
                     tagId,
@@ -1801,7 +1819,7 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = FrameworkStatsLog.CPU_TIME_PER_UID_FREQ;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {3})
+                .setAdditiveFields(new int[]{3})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1855,7 +1873,7 @@
         if (KernelCpuBpfTracking.isSupported()) {
             int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER;
             PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                    .setAdditiveFields(new int[] {3, 4})
+                    .setAdditiveFields(new int[]{3, 4})
                     .build();
             mStatsManager.setPullAtomCallback(
                     tagId,
@@ -1918,7 +1936,7 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = FrameworkStatsLog.CPU_ACTIVE_TIME;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2})
+                .setAdditiveFields(new int[]{2})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -1940,7 +1958,7 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         int tagId = FrameworkStatsLog.CPU_CLUSTER_TIME;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {3})
+                .setAdditiveFields(new int[]{3})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -2127,7 +2145,7 @@
     private void registerProcessMemoryState() {
         int tagId = FrameworkStatsLog.PROCESS_MEMORY_STATE;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {4, 5, 6, 7, 8})
+                .setAdditiveFields(new int[]{4, 5, 6, 7, 8})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -2485,7 +2503,7 @@
     private void registerBinderCallsStats() {
         int tagId = FrameworkStatsLog.BINDER_CALLS;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {4, 5, 6, 8, 12})
+                .setAdditiveFields(new int[]{4, 5, 6, 8, 12})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -2547,7 +2565,7 @@
     private void registerLooperStats() {
         int tagId = FrameworkStatsLog.LOOPER_STATS;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {5, 6, 7, 8, 9})
+                .setAdditiveFields(new int[]{5, 6, 7, 8, 9})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -2922,7 +2940,7 @@
     private void registerDiskIO() {
         int tagId = FrameworkStatsLog.DISK_IO;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
+                .setAdditiveFields(new int[]{2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
                 .setCoolDownMillis(3 * MILLIS_PER_SEC)
                 .build();
         mStatsManager.setPullAtomCallback(
@@ -2994,7 +3012,7 @@
     private void registerCpuTimePerThreadFreq() {
         int tagId = FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21})
+                .setAdditiveFields(new int[]{7, 9, 11, 13, 15, 17, 19, 21})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -3092,7 +3110,7 @@
     private void registerDebugElapsedClock() {
         int tagId = FrameworkStatsLog.DEBUG_ELAPSED_CLOCK;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {1, 2, 3, 4})
+                .setAdditiveFields(new int[]{1, 2, 3, 4})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -3127,7 +3145,7 @@
     private void registerDebugFailingElapsedClock() {
         int tagId = FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK;
         PullAtomMetadata metadata = new PullAtomMetadata.Builder()
-                .setAdditiveFields(new int[] {1, 2, 3, 4})
+                .setAdditiveFields(new int[]{1, 2, 3, 4})
                 .build();
         mStatsManager.setPullAtomCallback(
                 tagId,
@@ -3559,23 +3577,23 @@
                 int userId = users.get(userNum).getUserHandle().getIdentifier();
 
                 int unlockKeyguardEnabled = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1, userId);
                 int unlockDismissesKeyguard = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 1, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 1, userId);
                 int unlockAttentionRequired = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 0, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 0, userId);
                 int unlockAppEnabled = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId);
                 int unlockAlwaysRequireConfirmation = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0, userId);
                 int unlockDiversityRequired = Settings.Secure.getIntForUser(
-                          mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1, userId);
+                        mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1, userId);
 
                 pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
                         unlockKeyguardEnabled != 0, unlockDismissesKeyguard != 0,
@@ -4481,7 +4499,7 @@
                                     opacity));
                 }
             }
-        }  catch (RuntimeException e) {
+        } catch (RuntimeException e) {
             Slog.e(TAG, "pulling accessibility floating menu stats failed at getUsers", e);
             return StatsManager.PULL_SKIP;
         } finally {
@@ -4505,7 +4523,7 @@
         byte[] sinkSurroundEncodings = toBytes(audioManager.getReportedSurroundFormats());
         List<Integer> disabledSurroundEncodingsList = new ArrayList<>();
         List<Integer> enabledSurroundEncodingsList = new ArrayList<>();
-        for (int surroundEncoding:  surroundEncodingsMap.keySet()) {
+        for (int surroundEncoding : surroundEncodingsMap.keySet()) {
             if (!surroundEncodingsMap.get(surroundEncoding)) {
                 disabledSurroundEncodingsList.add(surroundEncoding);
             } else {
@@ -4574,6 +4592,46 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerPendingIntentsPerPackagePuller() {
+        int tagId = FrameworkStatsLog.PENDING_INTENTS_PER_PACKAGE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
+    private int pullPendingIntentsPerPackage(int atomTag, List<StatsEvent> pulledData) {
+        List<PendingIntentStats> pendingIntentStats =
+                LocalServices.getService(ActivityManagerInternal.class).getPendingIntentStats();
+        for (PendingIntentStats stats : pendingIntentStats) {
+            pulledData.add(FrameworkStatsLog.buildStatsEvent(
+                    atomTag, stats.uid, stats.count, stats.sizeKb));
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private void registerPinnerServiceStats() {
+        int tagId = FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullSystemServerPinnerStats(int atomTag, List<StatsEvent> pulledData) {
+        PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+        List<PinnedFileStats> pinnedFileStats = pinnerService.dumpDataForStatsd();
+        for (PinnedFileStats pfstats : pinnedFileStats) {
+            pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+                    pfstats.uid, pfstats.filename, pfstats.sizeKb));
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private byte[] toBytes(List<Integer> audioEncodings) {
         ProtoOutputStream protoOutputStream = new ProtoOutputStream();
         for (int audioEncoding : audioEncodings) {
@@ -4655,8 +4713,8 @@
      * string list.
      *
      * @param semicolonList colon-separated string, it should be
-     *                        {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} or
-     *                        {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
+     *                      {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} or
+     *                      {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
      * @return The number of accessibility services
      */
     private int countAccessibilityServices(String semicolonList) {
@@ -4778,5 +4836,4 @@
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 1dea3d7..fa243c0 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -17,6 +17,7 @@
 package com.android.server.trust;
 
 import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE;
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
 
 import android.annotation.TargetApi;
 import android.app.AlarmManager;
@@ -158,7 +159,7 @@
                     mMessage = (CharSequence) msg.obj;
                     int flags = msg.arg1;
                     mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
-                    if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
+                    if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
                         mWaitingForTrustableDowngrade = true;
                     } else {
                         mWaitingForTrustableDowngrade = false;
@@ -638,6 +639,11 @@
         return mTrustable && mManagingTrust && !mTrustDisabledByDpm;
     }
 
+    /** Set the trustagent as not trustable */
+    public void setUntrustable() {
+        mTrustable = false;
+    }
+
     public boolean isManagingTrust() {
         return mManagingTrust && !mTrustDisabledByDpm;
     }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index b4c54f9..bd4b8d1 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.trust;
 
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -127,13 +129,16 @@
     private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
     private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
     private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
-    public static final int MSG_USER_REQUESTED_UNLOCK = 16;
+    private static final int MSG_USER_REQUESTED_UNLOCK = 16;
+    private static final int MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH = 17;
 
     private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
 
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
     private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
     private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000;
+    private static final long TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS = 8 * 60 * 60 * 1000;
+    private static final long TRUSTABLE_TIMEOUT_IN_MILLIS = 24 * 60 * 60 * 1000;
 
     private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android";
 
@@ -203,9 +208,18 @@
     @GuardedBy("mUsersUnlockedByBiometric")
     private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
 
-    private final ArrayMap<Integer, TrustTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser =
+    private enum TimeoutType {
+        TRUSTED,
+        TRUSTABLE
+    }
+    private final ArrayMap<Integer, TrustedTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser =
             new ArrayMap<>();
+    private final SparseArray<TrustableTimeoutAlarmListener> mTrustableTimeoutAlarmListenerForUser =
+            new SparseArray<>();
+    private final SparseArray<TrustableTimeoutAlarmListener>
+            mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>();
     private AlarmManager mAlarmManager;
+    private final Object mAlarmLock = new Object();
     private final SettingsObserver mSettingsObserver;
 
     private final StrongAuthTracker mStrongAuthTracker;
@@ -258,7 +272,7 @@
 
         private final boolean mIsAutomotive;
         private final ContentResolver mContentResolver;
-        private boolean mTrustAgentsExtendUnlock;
+        private boolean mTrustAgentsNonrenewableTrust;
         private boolean mLockWhenTrustLost;
 
         /**
@@ -295,11 +309,11 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
-                // Smart lock should only extend unlock. The only exception is for automotive,
-                // where it can actively unlock the head unit.
+                // Smart lock should only grant non-renewable trust. The only exception is for
+                // automotive, where it can actively unlock the head unit.
                 int defaultValue = mIsAutomotive ? 0 : 1;
 
-                mTrustAgentsExtendUnlock =
+                mTrustAgentsNonrenewableTrust =
                         Settings.Secure.getIntForUser(
                                 mContentResolver,
                                 Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
@@ -315,8 +329,8 @@
             }
         }
 
-        boolean getTrustAgentsExtendUnlock() {
-            return mTrustAgentsExtendUnlock;
+        boolean getTrustAgentsNonrenewableTrust() {
+            return mTrustAgentsNonrenewableTrust;
         }
 
         boolean getLockWhenTrustLost() {
@@ -339,36 +353,53 @@
 
             // If active unlocking is not allowed, cancel any pending trust timeouts because the
             // screen is already locked.
-            TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
-            if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) {
+            TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+            if (alarm != null && mSettingsObserver.getTrustAgentsNonrenewableTrust()) {
                 mAlarmManager.cancel(alarm);
                 alarm.setQueued(false /* isQueued */);
             }
         }
     }
 
-    private void scheduleTrustTimeout(int userId, boolean override) {
+    private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) {
         int shouldOverride = override ? 1 : 0;
-        if (override) {
-            shouldOverride = 1;
-        }
-        mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget();
+        int trustableTimeout = isTrustableTimeout ? 1 : 0;
+        mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, shouldOverride,
+                trustableTimeout).sendToTarget();
     }
 
-    private void handleScheduleTrustTimeout(int userId, int shouldOverride) {
+    private void handleScheduleTrustTimeout(boolean shouldOverride, TimeoutType timeoutType) {
+        int userId = mCurrentUser;
+        if (timeoutType == TimeoutType.TRUSTABLE) {
+            // don't override the hard timeout unless biometric or knowledge factor authentication
+            // occurs which isn't where this is called from. Override the idle timeout what the
+            // calling function has determined.
+            handleScheduleTrustableTimeouts(userId, shouldOverride,
+                    false /* overrideHardTimeout */);
+        } else {
+            handleScheduleTrustedTimeout(userId, shouldOverride);
+        }
+    }
+
+    /* Override both the idle and hard trustable timeouts */
+    private void refreshTrustableTimers(int userId) {
+        handleScheduleTrustableTimeouts(userId, true /* overrideIdleTimeout */,
+                true /* overrideHardTimeout */);
+    }
+
+    private void handleScheduleTrustedTimeout(int userId, boolean shouldOverride) {
         long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS;
-        userId = mCurrentUser;
-        TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+        TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
 
         // Cancel existing trust timeouts for this user if needed.
         if (alarm != null) {
-            if (shouldOverride == 0 && alarm.isQueued()) {
+            if (!shouldOverride && alarm.isQueued()) {
                 if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping.");
                 return;
             }
             mAlarmManager.cancel(alarm);
         } else {
-            alarm = new TrustTimeoutAlarmListener(userId);
+            alarm = new TrustedTimeoutAlarmListener(userId);
             mTrustTimeoutAlarmListenerForUser.put(userId, alarm);
         }
 
@@ -379,6 +410,59 @@
                 mHandler);
     }
 
+    private void handleScheduleTrustableTimeouts(int userId, boolean overrideIdleTimeout,
+            boolean overrideHardTimeout) {
+        setUpIdleTimeout(userId, overrideIdleTimeout);
+        setUpHardTimeout(userId, overrideHardTimeout);
+    }
+
+    private void setUpIdleTimeout(int userId, boolean overrideIdleTimeout) {
+        long when = SystemClock.elapsedRealtime() + TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS;
+        TrustableTimeoutAlarmListener alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId);
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null);
+
+        // Cancel existing trustable timeouts for this user if needed.
+        if (alarm != null) {
+            if (!overrideIdleTimeout && alarm.isQueued()) {
+                if (DEBUG) Slog.d(TAG, "Found existing trustable timeout alarm. Skipping.");
+                return;
+            }
+            mAlarmManager.cancel(alarm);
+        } else {
+            alarm = new TrustableTimeoutAlarmListener(userId);
+            mIdleTrustableTimeoutAlarmListenerForUser.put(userId, alarm);
+        }
+
+        if (DEBUG) Slog.d(TAG, "\tSetting up trustable idle timeout alarm");
+        alarm.setQueued(true /* isQueued */);
+        mAlarmManager.setExact(
+                AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
+                mHandler);
+    }
+
+    private void setUpHardTimeout(int userId, boolean overrideHardTimeout) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null);
+        TrustableTimeoutAlarmListener alarm = mTrustableTimeoutAlarmListenerForUser.get(userId);
+
+        // if the alarm doesn't exist, or hasn't been queued, or needs to be overridden we need to
+        // set it
+        if (alarm == null || !alarm.isQueued() || overrideHardTimeout) {
+            // schedule hard limit on renewable trust use
+            long when = SystemClock.elapsedRealtime() + TRUSTABLE_TIMEOUT_IN_MILLIS;
+            if (alarm == null) {
+                alarm = new TrustableTimeoutAlarmListener(userId);
+                mTrustableTimeoutAlarmListenerForUser.put(userId, alarm);
+            } else if (overrideHardTimeout) {
+                mAlarmManager.cancel(alarm);
+            }
+            if (DEBUG) Slog.d(TAG, "\tSetting up trustable hard timeout alarm");
+            alarm.setQueued(true /* isQueued */);
+            mAlarmManager.setExact(
+                    AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
+                    mHandler);
+        }
+    }
+
    // Agent management
 
     private static final class AgentInfo {
@@ -419,11 +503,11 @@
         if (ENABLE_ACTIVE_UNLOCK_FLAG) {
             updateTrustWithRenewableUnlock(userId, flags, isFromUnlock);
         } else {
-            updateTrustWithExtendUnlock(userId, flags, isFromUnlock);
+            updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock);
         }
     }
 
-    private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) {
+    private void updateTrustWithNonrenewableTrust(int userId, int flags, boolean isFromUnlock) {
         boolean managed = aggregateIsTrustManaged(userId);
         dispatchOnTrustManagedChanged(managed, userId);
         if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -441,8 +525,8 @@
 
         boolean changed;
         synchronized (mUserIsTrusted) {
-            if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
-                // In extend unlock trust agents can only set the device to trusted if it already
+            if (mSettingsObserver.getTrustAgentsNonrenewableTrust()) {
+                // For non-renewable trust agents can only set the device to trusted if it already
                 // trusted or the device is unlocked. Attempting to set the device as trusted
                 // when the device is locked will be ignored.
                 changed = mUserIsTrusted.get(userId) != trusted;
@@ -464,7 +548,7 @@
             if (!trusted) {
                 maybeLockScreen(userId);
             } else {
-                scheduleTrustTimeout(userId, false /* override */);
+                scheduleTrustTimeout(false /* override */, false /* isTrustableTimeout*/);
             }
         }
     }
@@ -522,7 +606,12 @@
             if (!isNowTrusted) {
                 maybeLockScreen(userId);
             } else {
-                scheduleTrustTimeout(userId, false /* override */);
+                boolean isTrustableTimeout =
+                        (flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0;
+                // Every time we grant renewable trust we should override the idle trustable
+                // timeout. If this is for non-renewable trust, then we shouldn't override.
+                scheduleTrustTimeout(isTrustableTimeout /* override */,
+                        isTrustableTimeout /* isTrustableTimeout */);
             }
         }
     }
@@ -1102,8 +1191,9 @@
     private void dispatchUnlockAttempt(boolean successful, int userId) {
         if (successful) {
             mStrongAuthTracker.allowTrustFromUnlock(userId);
-            // Allow the presence of trust on a successful unlock attempt to extend unlock.
+            // Allow the presence of trust on a successful unlock attempt to extend unlock
             updateTrust(userId, 0 /* flags */, true);
+            mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget();
         }
 
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -1520,11 +1610,12 @@
             synchronized(mUsersUnlockedByBiometric) {
                 mUsersUnlockedByBiometric.put(userId, true);
             }
-            // In extend unlock mode we need to refresh trust state here, which will call
+            // In non-renewable trust mode we need to refresh trust state here, which will call
             // refreshDeviceLockedForUser()
-            int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0;
+            int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsNonrenewableTrust() ? 1 : 0;
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
                     updateTrustOnUnlock).sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget();
         }
 
         @Override
@@ -1643,7 +1734,13 @@
                     refreshDeviceLockedForUser(msg.arg1, unlockedUser);
                     break;
                 case MSG_SCHEDULE_TRUST_TIMEOUT:
-                    handleScheduleTrustTimeout(msg.arg1, msg.arg2);
+                    boolean shouldOverride = msg.arg1 == 1 ? true : false;
+                    TimeoutType timeoutType =
+                            msg.arg2 == 1 ? TimeoutType.TRUSTABLE : TimeoutType.TRUSTED;
+                    handleScheduleTrustTimeout(shouldOverride, timeoutType);
+                    break;
+                case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH:
+                    refreshTrustableTimers(msg.arg1);
                     break;
             }
         }
@@ -1759,10 +1856,11 @@
             // Cancel pending alarms if we require some auth anyway.
             if (!isTrustAllowedForUser(userId)) {
                 TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
-                if (alarm != null && alarm.isQueued()) {
-                    alarm.setQueued(false /* isQueued */);
-                    mAlarmManager.cancel(alarm);
-                }
+                cancelPendingAlarm(alarm);
+                alarm = mTrustableTimeoutAlarmListenerForUser.get(userId);
+                cancelPendingAlarm(alarm);
+                alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId);
+                cancelPendingAlarm(alarm);
             }
 
             refreshAgentList(userId);
@@ -1772,6 +1870,13 @@
             updateTrust(userId, 0 /* flags */);
         }
 
+        private void cancelPendingAlarm(@Nullable TrustTimeoutAlarmListener alarm) {
+            if (alarm != null && alarm.isQueued()) {
+                alarm.setQueued(false /* isQueued */);
+                mAlarmManager.cancel(alarm);
+            }
+        }
+
         boolean canAgentsRunForUser(int userId) {
             return mStartFromSuccessfulUnlock.get(userId)
                     || super.isTrustAllowedForUser(userId);
@@ -1804,9 +1909,9 @@
         }
     }
 
-    private class TrustTimeoutAlarmListener implements OnAlarmListener {
-        private final int mUserId;
-        private boolean mIsQueued = false;
+    private abstract class TrustTimeoutAlarmListener implements OnAlarmListener {
+        protected final int mUserId;
+        protected boolean mIsQueued = false;
 
         TrustTimeoutAlarmListener(int userId) {
             mUserId = userId;
@@ -1815,8 +1920,7 @@
         @Override
         public void onAlarm() {
             mIsQueued = false;
-            int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId);
-
+            handleAlarm();
             // Only fire if trust can unlock.
             if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
                 if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
@@ -1826,12 +1930,98 @@
             maybeLockScreen(mUserId);
         }
 
-        public void setQueued(boolean isQueued) {
-            mIsQueued = isQueued;
-        }
+        protected abstract void handleAlarm();
 
         public boolean isQueued() {
             return mIsQueued;
         }
+
+        public void setQueued(boolean isQueued) {
+            mIsQueued = isQueued;
+        }
+    }
+
+    private class TrustedTimeoutAlarmListener extends TrustTimeoutAlarmListener {
+
+        TrustedTimeoutAlarmListener(int userId) {
+            super(userId);
+        }
+
+        @Override
+        public void handleAlarm() {
+            TrustableTimeoutAlarmListener otherAlarm;
+            boolean otherAlarmPresent;
+            if (ENABLE_ACTIVE_UNLOCK_FLAG) {
+                otherAlarm = mTrustableTimeoutAlarmListenerForUser.get(mUserId);
+                otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued();
+                if (otherAlarmPresent) {
+                    synchronized (mAlarmLock) {
+                        disableNonrenewableTrustWhileRenewableTrustIsPresent();
+                    }
+                    return;
+                }
+            }
+        }
+
+        private void disableNonrenewableTrustWhileRenewableTrustIsPresent() {
+            synchronized (mUserTrustState) {
+                if (mUserTrustState.get(mUserId) == TrustState.TRUSTED) {
+                    // if we're trusted and we have a trustable alarm, we need to
+                    // downgrade to trustable
+                    mUserTrustState.put(mUserId, TrustState.TRUSTABLE);
+                    updateTrust(mUserId, 0 /* flags */);
+                }
+            }
+        }
+    }
+
+    private class TrustableTimeoutAlarmListener extends TrustTimeoutAlarmListener {
+
+        TrustableTimeoutAlarmListener(int userId) {
+            super(userId);
+        }
+
+        @Override
+        public void handleAlarm() {
+            TrustedTimeoutAlarmListener otherAlarm;
+            boolean otherAlarmPresent;
+            if (ENABLE_ACTIVE_UNLOCK_FLAG) {
+                cancelBothTrustableAlarms();
+                otherAlarm = mTrustTimeoutAlarmListenerForUser.get(mUserId);
+                otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued();
+                if (otherAlarmPresent) {
+                    synchronized (mAlarmLock) {
+                        disableRenewableTrustWhileNonrenewableTrustIsPresent();
+                    }
+                    return;
+                }
+            }
+        }
+
+        private void cancelBothTrustableAlarms() {
+            TrustableTimeoutAlarmListener idleTimeout =
+                    mIdleTrustableTimeoutAlarmListenerForUser.get(
+                            mUserId);
+            TrustableTimeoutAlarmListener trustableTimeout =
+                    mTrustableTimeoutAlarmListenerForUser.get(
+                            mUserId);
+            if (idleTimeout != null && idleTimeout.isQueued()) {
+                idleTimeout.setQueued(false);
+                mAlarmManager.cancel(idleTimeout);
+            }
+            if (trustableTimeout != null && trustableTimeout.isQueued()) {
+                trustableTimeout.setQueued(false);
+                mAlarmManager.cancel(trustableTimeout);
+            }
+        }
+
+        private void disableRenewableTrustWhileNonrenewableTrustIsPresent() {
+            // if non-renewable trust is running, we need to temporarily prevent
+            // renewable trust from being used
+            for (AgentInfo agentInfo : mActiveAgents) {
+                agentInfo.agent.setUntrustable();
+            }
+            updateTrust(mUserId, 0 /* flags */);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e02fabd..b95d372 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -102,6 +102,8 @@
 import com.android.server.IoThread;
 import com.android.server.SystemService;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -2639,6 +2641,7 @@
             }
         }
 
+        @NeverCompile // Avoid size overhead of debugging code.
         @Override
         @SuppressWarnings("resource")
         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index a17e792..30e2617 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -146,7 +146,7 @@
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
 
-        mContext.registerReceiver(this, filter, null, mHandler);
+        mContext.registerReceiver(this, filter, null, mHandler, Context.RECEIVER_NOT_EXPORTED);
         mSubscriptionManager.addOnSubscriptionsChangedListener(
                 executor, mSubscriptionChangedListener);
         mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 8b80b4a..597f7f2 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -78,6 +78,7 @@
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -163,6 +164,14 @@
 public class VcnGatewayConnection extends StateMachine {
     private static final String TAG = VcnGatewayConnection.class.getSimpleName();
 
+    // Matches DataConnection.NETWORK_TYPE private constant, and magic string from
+    // ConnectivityManager#getNetworkTypeName()
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final String NETWORK_INFO_NETWORK_TYPE_STRING = "MOBILE";
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final String NETWORK_INFO_EXTRA_INFO = "VCN";
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
 
@@ -1631,6 +1640,12 @@
             final NetworkAgentConfig nac =
                     new NetworkAgentConfig.Builder()
                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
+                            .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING)
+                            .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+                            .setLegacySubTypeName(
+                                    TelephonyManager.getNetworkTypeName(
+                                            TelephonyManager.NETWORK_TYPE_UNKNOWN))
+                            .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO)
                             .build();
 
             final VcnNetworkAgent agent =
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
new file mode 100644
index 0000000..3550bda
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -0,0 +1,148 @@
+/*
+ * 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.vibrator;
+
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represent a step on a single vibrator that plays one or more segments from a
+ * {@link VibrationEffect.Composed} effect.
+ */
+abstract class AbstractVibratorStep extends Step {
+    public final VibratorController controller;
+    public final VibrationEffect.Composed effect;
+    public final int segmentIndex;
+    public final long previousStepVibratorOffTimeout;
+
+    long mVibratorOnResult;
+    boolean mVibratorCompleteCallbackReceived;
+
+    /**
+     * @param conductor          The VibrationStepConductor for these steps.
+     * @param startTime          The time to schedule this step in the
+     *                           {@link VibrationStepConductor}.
+     * @param controller         The vibrator that is playing the effect.
+     * @param effect             The effect being played in this step.
+     * @param index              The index of the next segment to be played by this step
+     * @param previousStepVibratorOffTimeout The time the vibrator is expected to complete any
+     *                           previous vibration and turn off. This is used to allow this step to
+     *                           be triggered when the completion callback is received, and can
+     *                           be used to play effects back-to-back.
+     */
+    AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long previousStepVibratorOffTimeout) {
+        super(conductor, startTime);
+        this.controller = controller;
+        this.effect = effect;
+        this.segmentIndex = index;
+        this.previousStepVibratorOffTimeout = previousStepVibratorOffTimeout;
+    }
+
+    public int getVibratorId() {
+        return controller.getVibratorInfo().getId();
+    }
+
+    @Override
+    public long getVibratorOnDuration() {
+        return mVibratorOnResult;
+    }
+
+    @Override
+    public boolean acceptVibratorCompleteCallback(int vibratorId) {
+        boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
+        mVibratorCompleteCallbackReceived |= isSameVibrator;
+        // Only activate this step if a timeout was set to wait for the vibration to complete,
+        // otherwise we are waiting for the correct time to play the next step.
+        return isSameVibrator && (previousStepVibratorOffTimeout > SystemClock.uptimeMillis());
+    }
+
+    @Override
+    public List<Step> cancel() {
+        return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
+                /* cancelled= */ true, controller, previousStepVibratorOffTimeout));
+    }
+
+    @Override
+    public void cancelImmediately() {
+        if (previousStepVibratorOffTimeout > SystemClock.uptimeMillis()) {
+            // Vibrator might be running from previous steps, so turn it off while canceling.
+            stopVibrating();
+        }
+    }
+
+    protected void stopVibrating() {
+        if (VibrationThread.DEBUG) {
+            Slog.d(VibrationThread.TAG,
+                    "Turning off vibrator " + getVibratorId());
+        }
+        controller.off();
+    }
+
+    protected void changeAmplitude(float amplitude) {
+        if (VibrationThread.DEBUG) {
+            Slog.d(VibrationThread.TAG,
+                    "Amplitude changed on vibrator " + getVibratorId() + " to " + amplitude);
+        }
+        controller.setAmplitude(amplitude);
+    }
+
+    /**
+     * Return the {@link VibrationStepConductor#nextVibrateStep} with same timings, only jumping
+     * the segments.
+     */
+    protected List<Step> skipToNextSteps(int segmentsSkipped) {
+        return nextSteps(startTime, previousStepVibratorOffTimeout, segmentsSkipped);
+    }
+
+    /**
+     * Return the {@link VibrationStepConductor#nextVibrateStep} with same start and off timings
+     * calculated from {@link #getVibratorOnDuration()}, jumping all played segments.
+     *
+     * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
+     * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
+     */
+    protected List<Step> nextSteps(int segmentsPlayed) {
+        if (mVibratorOnResult <= 0) {
+            // Vibration was not started, so just skip the played segments and keep timings.
+            return skipToNextSteps(segmentsPlayed);
+        }
+        long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
+        long nextVibratorOffTimeout =
+                nextStartTime + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+        return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
+    }
+
+    /**
+     * Return the {@link VibrationStepConductor#nextVibrateStep} with given start and off timings,
+     * which might be calculated independently, jumping all played segments.
+     *
+     * <p>This should be used when the vibrator on/off state is not responsible for the steps
+     * execution timings, e.g. while playing the vibrator amplitudes.
+     */
+    protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
+            int segmentsPlayed) {
+        Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
+                segmentIndex + segmentsPlayed, vibratorOffTimeout);
+        return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
new file mode 100644
index 0000000..8585e34
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java
@@ -0,0 +1,114 @@
+/*
+ * 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.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to complete a {@link VibrationEffect}.
+ *
+ * <p>This runs right at the time the vibration is considered to end and will update the pending
+ * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
+ */
+final class CompleteEffectVibratorStep extends AbstractVibratorStep {
+    private final boolean mCancelled;
+
+    CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
+            VibratorController controller, long previousStepVibratorOffTimeout) {
+        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
+                previousStepVibratorOffTimeout);
+        mCancelled = cancelled;
+    }
+
+    @Override
+    public boolean isCleanUp() {
+        // If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
+        // Otherwise this step is part of the vibration.
+        return mCancelled;
+    }
+
+    @Override
+    public List<Step> cancel() {
+        if (mCancelled) {
+            // Double cancelling will just turn off the vibrator right away.
+            return Arrays.asList(
+                    new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+        }
+        return super.cancel();
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteEffectVibratorStep");
+        try {
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
+                                + " step on vibrator " + controller.getVibratorInfo().getId());
+            }
+            if (mVibratorCompleteCallbackReceived) {
+                // Vibration completion callback was received by this step, just turn if off
+                // and skip any clean-up.
+                stopVibrating();
+                return VibrationStepConductor.EMPTY_STEP_LIST;
+            }
+
+            float currentAmplitude = controller.getCurrentAmplitude();
+            long remainingOnDuration =
+                    previousStepVibratorOffTimeout - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT
+                            - SystemClock.uptimeMillis();
+            long rampDownDuration =
+                    Math.min(remainingOnDuration,
+                            conductor.vibrationSettings.getRampDownDuration());
+            long stepDownDuration = conductor.vibrationSettings.getRampStepDuration();
+            if (currentAmplitude < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN
+                    || rampDownDuration <= stepDownDuration) {
+                // No need to ramp down the amplitude, just wait to turn it off.
+                if (mCancelled) {
+                    // Vibration is completing because it was cancelled, turn off right away.
+                    stopVibrating();
+                    return VibrationStepConductor.EMPTY_STEP_LIST;
+                } else {
+                    return Arrays.asList(new TurnOffVibratorStep(
+                            conductor, previousStepVibratorOffTimeout, controller));
+                }
+            }
+
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "Ramping down vibrator " + controller.getVibratorInfo().getId()
+                                + " from amplitude " + currentAmplitude
+                                + " for " + rampDownDuration + "ms");
+            }
+            float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
+            float amplitudeTarget = currentAmplitude - amplitudeDelta;
+            long newVibratorOffTimeout =
+                    mCancelled ? rampDownDuration : previousStepVibratorOffTimeout;
+            return Arrays.asList(
+                    new RampOffVibratorStep(conductor, startTime, amplitudeTarget, amplitudeDelta,
+                            controller, newVibratorOffTimeout));
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
new file mode 100644
index 0000000..d1ea805
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -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.server.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on using a composition of primitives.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link PrimitiveSegment} starting at the current index.
+ */
+final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
+
+    ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long previousStepVibratorOffTimeout) {
+        // This step should wait for the last vibration to finish (with the timeout) and for the
+        // intended step start time (to respect the effect delays).
+        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+                index, previousStepVibratorOffTimeout);
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
+        try {
+            // Load the next PrimitiveSegments to create a single compose call to the vibrator,
+            // limited to the vibrator composition maximum size.
+            int limit = controller.getVibratorInfo().getCompositionSizeMax();
+            int segmentCount = limit > 0
+                    ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+                    : effect.getSegments().size();
+            List<PrimitiveSegment> primitives = new ArrayList<>();
+            for (int i = segmentIndex; i < segmentCount; i++) {
+                VibrationEffectSegment segment = effect.getSegments().get(i);
+                if (segment instanceof PrimitiveSegment) {
+                    primitives.add((PrimitiveSegment) segment);
+                } else {
+                    break;
+                }
+            }
+
+            if (primitives.isEmpty()) {
+                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
+                        + effect.getSegments().get(segmentIndex));
+                return skipToNextSteps(/* segmentsSkipped= */ 1);
+            }
+
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
+                        + controller.getVibratorInfo().getId());
+            }
+            mVibratorOnResult = controller.on(
+                    primitives.toArray(new PrimitiveSegment[primitives.size()]),
+                    getVibration().id);
+
+            return nextSteps(/* segmentsPlayed= */ primitives.size());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
new file mode 100644
index 0000000..73bf933
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -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.server.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on using a composition of PWLE segments.
+ *
+ * <p>This step will use the maximum supported number of consecutive segments of type
+ * {@link StepSegment} or {@link RampSegment} starting at the current index.
+ */
+final class ComposePwleVibratorStep extends AbstractVibratorStep {
+
+    ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long previousStepVibratorOffTimeout) {
+        // This step should wait for the last vibration to finish (with the timeout) and for the
+        // intended step start time (to respect the effect delays).
+        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+                index, previousStepVibratorOffTimeout);
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
+        try {
+            // Load the next RampSegments to create a single composePwle call to the vibrator,
+            // limited to the vibrator PWLE maximum size.
+            int limit = controller.getVibratorInfo().getPwleSizeMax();
+            int segmentCount = limit > 0
+                    ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+                    : effect.getSegments().size();
+            List<RampSegment> pwles = new ArrayList<>();
+            for (int i = segmentIndex; i < segmentCount; i++) {
+                VibrationEffectSegment segment = effect.getSegments().get(i);
+                if (segment instanceof RampSegment) {
+                    pwles.add((RampSegment) segment);
+                } else {
+                    break;
+                }
+            }
+
+            if (pwles.isEmpty()) {
+                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
+                        + effect.getSegments().get(segmentIndex));
+                return skipToNextSteps(/* segmentsSkipped= */ 1);
+            }
+
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG, "Compose " + pwles + " PWLEs on vibrator "
+                        + controller.getVibratorInfo().getId());
+            }
+            mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
+                    getVibration().id);
+
+            return nextSteps(/* segmentsPlayed= */ pwles.size());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
new file mode 100644
index 0000000..bbbca02
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java
@@ -0,0 +1,73 @@
+/*
+ * 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.vibrator;
+
+import android.os.Trace;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Finish a sync vibration started by a {@link StartSequentialEffectStep}.
+ *
+ * <p>This only plays after all active vibrators steps have finished, and adds a {@link
+ * StartSequentialEffectStep} to the queue if the sequential effect isn't finished yet.
+ */
+final class FinishSequentialEffectStep extends Step {
+    public final StartSequentialEffectStep startedStep;
+
+    FinishSequentialEffectStep(StartSequentialEffectStep startedStep) {
+        // No predefined startTime, just wait for all steps in the queue.
+        super(startedStep.conductor, Long.MAX_VALUE);
+        this.startedStep = startedStep;
+    }
+
+    @Override
+    public boolean isCleanUp() {
+        // This step only notes that all the vibrators has been turned off.
+        return true;
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishSequentialEffectStep");
+        try {
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "FinishSequentialEffectStep for effect #" + startedStep.currentIndex);
+            }
+            conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+            Step nextStep = startedStep.nextStep();
+            return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST
+                    : Arrays.asList(nextStep);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+
+    @Override
+    public List<Step> cancel() {
+        cancelImmediately();
+        return VibrationStepConductor.EMPTY_STEP_LIST;
+    }
+
+    @Override
+    public void cancelImmediately() {
+        conductor.vibratorManagerHooks.noteVibratorOff(conductor.getVibration().uid);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
new file mode 100644
index 0000000..601ae97
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -0,0 +1,103 @@
+/*
+ * 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.vibrator;
+
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on with a single prebaked effect.
+ *
+ * <p>This step automatically falls back by replacing the prebaked segment with
+ * {@link VibrationSettings#getFallbackEffect(int)}, if available.
+ */
+final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
+
+    PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long previousStepVibratorOffTimeout) {
+        // This step should wait for the last vibration to finish (with the timeout) and for the
+        // intended step start time (to respect the effect delays).
+        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
+                index, previousStepVibratorOffTimeout);
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformPrebakedVibratorStep");
+        try {
+            VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+            if (!(segment instanceof PrebakedSegment)) {
+                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a "
+                        + "PerformPrebakedVibratorStep: " + segment);
+                return skipToNextSteps(/* segmentsSkipped= */ 1);
+            }
+
+            PrebakedSegment prebaked = (PrebakedSegment) segment;
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG, "Perform " + VibrationEffect.effectIdToString(
+                        prebaked.getEffectId()) + " on vibrator "
+                        + controller.getVibratorInfo().getId());
+            }
+
+            VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
+            mVibratorOnResult = controller.on(prebaked, getVibration().id);
+
+            if (mVibratorOnResult == 0 && prebaked.shouldFallback()
+                    && (fallback instanceof VibrationEffect.Composed)) {
+                if (VibrationThread.DEBUG) {
+                    Slog.d(VibrationThread.TAG, "Playing fallback for effect "
+                            + VibrationEffect.effectIdToString(prebaked.getEffectId()));
+                }
+                AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
+                        replaceCurrentSegment((VibrationEffect.Composed) fallback),
+                        segmentIndex, previousStepVibratorOffTimeout);
+                List<Step> fallbackResult = fallbackStep.play();
+                // Update the result with the fallback result so this step is seamlessly
+                // replaced by the fallback to any outer application of this.
+                mVibratorOnResult = fallbackStep.getVibratorOnDuration();
+                return fallbackResult;
+            }
+
+            return nextSteps(/* segmentsPlayed= */ 1);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+
+    /**
+     * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
+     *
+     * @return a copy of {@link #effect} with replaced segment.
+     */
+    private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
+        List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
+        int newRepeatIndex = effect.getRepeatIndex();
+        newSegments.remove(segmentIndex);
+        newSegments.addAll(segmentIndex, fallback.getSegments());
+        if (segmentIndex < effect.getRepeatIndex()) {
+            newRepeatIndex += fallback.getSegments().size() - 1;
+        }
+        return new VibrationEffect.Composed(newSegments, newRepeatIndex);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
new file mode 100644
index 0000000..8cf5fb3
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java
@@ -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.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** Represents a step to ramp down the vibrator amplitude before turning it off. */
+final class RampOffVibratorStep extends AbstractVibratorStep {
+    private final float mAmplitudeTarget;
+    private final float mAmplitudeDelta;
+
+    RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget,
+            float amplitudeDelta, VibratorController controller,
+            long previousStepVibratorOffTimeout) {
+        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
+                previousStepVibratorOffTimeout);
+        mAmplitudeTarget = amplitudeTarget;
+        mAmplitudeDelta = amplitudeDelta;
+    }
+
+    @Override
+    public boolean isCleanUp() {
+        return true;
+    }
+
+    @Override
+    public List<Step> cancel() {
+        return Arrays.asList(
+                new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffVibratorStep");
+        try {
+            if (VibrationThread.DEBUG) {
+                long latency = SystemClock.uptimeMillis() - startTime;
+                Slog.d(VibrationThread.TAG, "Ramp down the vibrator amplitude, step with "
+                        + latency + "ms latency.");
+            }
+            if (mVibratorCompleteCallbackReceived) {
+                // Vibration completion callback was received by this step, just turn if off
+                // and skip the rest of the steps to ramp down the vibrator amplitude.
+                stopVibrating();
+                return VibrationStepConductor.EMPTY_STEP_LIST;
+            }
+
+            changeAmplitude(mAmplitudeTarget);
+
+            float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
+            if (newAmplitudeTarget < VibrationStepConductor.RAMP_OFF_AMPLITUDE_MIN) {
+                // Vibrator amplitude cannot go further down, just turn it off.
+                return Arrays.asList(new TurnOffVibratorStep(
+                        conductor, previousStepVibratorOffTimeout, controller));
+            }
+            return Arrays.asList(new RampOffVibratorStep(
+                    conductor,
+                    startTime + conductor.vibrationSettings.getRampStepDuration(),
+                    newAmplitudeTarget, mAmplitudeDelta, controller,
+                    previousStepVibratorOffTimeout));
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
new file mode 100644
index 0000000..d5c1116
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator on and change its amplitude.
+ *
+ * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
+ * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
+ */
+final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
+    private long mNextOffTime;
+
+    SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller, VibrationEffect.Composed effect, int index,
+            long previousStepVibratorOffTimeout) {
+        // This step has a fixed startTime coming from the timings of the waveform it's playing.
+        super(conductor, startTime, controller, effect, index, previousStepVibratorOffTimeout);
+        mNextOffTime = previousStepVibratorOffTimeout;
+    }
+
+    @Override
+    public boolean acceptVibratorCompleteCallback(int vibratorId) {
+        if (controller.getVibratorInfo().getId() == vibratorId) {
+            mVibratorCompleteCallbackReceived = true;
+            mNextOffTime = SystemClock.uptimeMillis();
+        }
+        // Timings are tightly controlled here, so only trigger this step if the vibrator was
+        // supposed to be ON but has completed prematurely, to turn it back on as soon as
+        // possible.
+        return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
+    }
+
+    @Override
+    public List<Step> play() {
+        // TODO: consider separating the "on" steps at the start into a separate Step.
+        // TODO: consider instantiating the step with the required amplitude, rather than
+        // needing to dig into the effect.
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SetAmplitudeVibratorStep");
+        try {
+            long now = SystemClock.uptimeMillis();
+            long latency = now - startTime;
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "Running amplitude step with " + latency + "ms latency.");
+            }
+
+            if (mVibratorCompleteCallbackReceived && latency < 0) {
+                // This step was run early because the vibrator turned off prematurely.
+                // Turn it back on and return this same step to run at the exact right time.
+                mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
+                return Arrays.asList(new SetAmplitudeVibratorStep(conductor, startTime, controller,
+                        effect, segmentIndex, mNextOffTime));
+            }
+
+            VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+            if (!(segment instanceof StepSegment)) {
+                Slog.w(VibrationThread.TAG,
+                        "Ignoring wrong segment for a SetAmplitudeVibratorStep: " + segment);
+                return skipToNextSteps(/* segmentsSkipped= */ 1);
+            }
+
+            StepSegment stepSegment = (StepSegment) segment;
+            if (stepSegment.getDuration() == 0) {
+                // Skip waveform entries with zero timing.
+                return skipToNextSteps(/* segmentsSkipped= */ 1);
+            }
+
+            float amplitude = stepSegment.getAmplitude();
+            if (amplitude == 0) {
+                if (previousStepVibratorOffTimeout > now) {
+                    // Amplitude cannot be set to zero, so stop the vibrator.
+                    stopVibrating();
+                    mNextOffTime = now;
+                }
+            } else {
+                if (startTime >= mNextOffTime) {
+                    // Vibrator is OFF. Turn vibrator back on for the duration of another
+                    // cycle before setting the amplitude.
+                    long onDuration = getVibratorOnDuration(effect, segmentIndex);
+                    if (onDuration > 0) {
+                        mVibratorOnResult = startVibrating(onDuration);
+                        mNextOffTime = now + onDuration
+                                + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+                    }
+                }
+                changeAmplitude(amplitude);
+            }
+
+            // Use original startTime to avoid propagating latencies to the waveform.
+            long nextStartTime = startTime + segment.getDuration();
+            return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+
+    private long turnVibratorBackOn(long remainingDuration) {
+        long onDuration = getVibratorOnDuration(effect, segmentIndex);
+        if (onDuration <= 0) {
+            // Vibrator is supposed to go back off when this step starts, so just leave it off.
+            return previousStepVibratorOffTimeout;
+        }
+        onDuration += remainingDuration;
+        float expectedAmplitude = controller.getCurrentAmplitude();
+        mVibratorOnResult = startVibrating(onDuration);
+        if (mVibratorOnResult > 0) {
+            // Set the amplitude back to the value it was supposed to be playing at.
+            changeAmplitude(expectedAmplitude);
+        }
+        return SystemClock.uptimeMillis() + onDuration
+                + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
+    }
+
+    private long startVibrating(long duration) {
+        if (VibrationThread.DEBUG) {
+            Slog.d(VibrationThread.TAG,
+                    "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+                            + duration + "ms");
+        }
+        return controller.on(duration, getVibration().id);
+    }
+
+    /**
+     * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
+     * until the next time it's vibrating amplitude is zero or a different type of segment is
+     * found.
+     */
+    private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
+        List<VibrationEffectSegment> segments = effect.getSegments();
+        int segmentCount = segments.size();
+        int repeatIndex = effect.getRepeatIndex();
+        int i = startIndex;
+        long timing = 0;
+        while (i < segmentCount) {
+            VibrationEffectSegment segment = segments.get(i);
+            if (!(segment instanceof StepSegment)
+                    || ((StepSegment) segment).getAmplitude() == 0) {
+                break;
+            }
+            timing += segment.getDuration();
+            i++;
+            if (i == segmentCount && repeatIndex >= 0) {
+                i = repeatIndex;
+                // prevent infinite loop
+                repeatIndex = -1;
+            }
+            if (i == startIndex) {
+                // The repeating waveform keeps the vibrator ON all the time. Use a minimum
+                // of 1s duration to prevent short patterns from turning the vibrator ON too
+                // frequently.
+                return Math.max(timing, 1000);
+            }
+        }
+        if (i == segmentCount && effect.getRepeatIndex() < 0) {
+            // Vibration ending at non-zero amplitude, add extra timings to ramp down after
+            // vibration is complete.
+            timing += conductor.vibrationSettings.getRampDownDuration();
+        }
+        return timing;
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java
new file mode 100644
index 0000000..b8885e8
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.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.server.vibrator;
+
+import android.annotation.Nullable;
+import android.hardware.vibrator.IVibratorManager;
+import android.os.CombinedVibration;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Starts a sync vibration.
+ *
+ * <p>If this step has successfully started playing a vibration on any vibrator, it will always
+ * add a {@link FinishSequentialEffectStep} to the queue, to be played after all vibrators
+ * have finished all their individual steps.
+ *
+ * <p>If this step does not start any vibrator, it will add a {@link StartSequentialEffectStep} if
+ * the sequential effect isn't finished yet.
+ *
+ * <p>TODO: this step actually does several things: multiple HAL calls to sync the vibrators,
+ * as well as dispatching the underlying vibrator instruction calls (which need to be done before
+ * triggering the synced effects). This role/encapsulation could probably be improved to split up
+ * the grouped HAL calls here, as well as to clarify the role of dispatching VibratorSteps between
+ * this class and the controller.
+ */
+final class StartSequentialEffectStep extends Step {
+    public final CombinedVibration.Sequential sequentialEffect;
+    public final int currentIndex;
+
+    private long mVibratorsOnMaxDuration;
+
+    StartSequentialEffectStep(VibrationStepConductor conductor,
+            CombinedVibration.Sequential effect) {
+        this(conductor, SystemClock.uptimeMillis() + effect.getDelays().get(0), effect,
+                /* index= */ 0);
+    }
+
+    StartSequentialEffectStep(VibrationStepConductor conductor, long startTime,
+            CombinedVibration.Sequential effect, int index) {
+        super(conductor, startTime);
+        sequentialEffect = effect;
+        currentIndex = index;
+    }
+
+    @Override
+    public long getVibratorOnDuration() {
+        return mVibratorsOnMaxDuration;
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartSequentialEffectStep");
+        List<Step> nextSteps = new ArrayList<>();
+        mVibratorsOnMaxDuration = -1;
+        try {
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "StartSequentialEffectStep for effect #" + currentIndex);
+            }
+            CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex);
+            DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
+            if (effectMapping == null) {
+                // Unable to map effects to vibrators, ignore this step.
+                return nextSteps;
+            }
+
+            mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
+            if (mVibratorsOnMaxDuration > 0) {
+                conductor.vibratorManagerHooks.noteVibratorOn(conductor.getVibration().uid,
+                        mVibratorsOnMaxDuration);
+            }
+        } finally {
+            if (mVibratorsOnMaxDuration >= 0) {
+                // It least one vibrator was started then add a finish step to wait for all
+                // active vibrators to finish their individual steps before going to the next.
+                // Otherwise this step was ignored so just go to the next one.
+                Step nextStep =
+                        mVibratorsOnMaxDuration > 0 ? new FinishSequentialEffectStep(this)
+                                : nextStep();
+                if (nextStep != null) {
+                    nextSteps.add(nextStep);
+                }
+            }
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+        return nextSteps;
+    }
+
+    @Override
+    public List<Step> cancel() {
+        return VibrationStepConductor.EMPTY_STEP_LIST;
+    }
+
+    @Override
+    public void cancelImmediately() {
+    }
+
+    /**
+     * Create the next {@link StartSequentialEffectStep} to play this sequential effect, starting at
+     * the
+     * time this method is called, or null if sequence is complete.
+     */
+    @Nullable
+    Step nextStep() {
+        int nextIndex = currentIndex + 1;
+        if (nextIndex >= sequentialEffect.getEffects().size()) {
+            return null;
+        }
+        long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
+        long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
+        return new StartSequentialEffectStep(conductor, nextStartTime, sequentialEffect,
+                nextIndex);
+    }
+
+    /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
+    @Nullable
+    private DeviceEffectMap createEffectToVibratorMapping(
+            CombinedVibration effect) {
+        if (effect instanceof CombinedVibration.Mono) {
+            return new DeviceEffectMap((CombinedVibration.Mono) effect);
+        }
+        if (effect instanceof CombinedVibration.Stereo) {
+            return new DeviceEffectMap((CombinedVibration.Stereo) effect);
+        }
+        return null;
+    }
+
+    /**
+     * Starts playing effects on designated vibrators, in sync.
+     *
+     * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators
+     * @param nextSteps     An output list to accumulate the future {@link Step
+     *                      Steps} created
+     *                      by this method, typically one for each vibrator that has
+     *                      successfully started vibrating on this step.
+     * @return The duration, in millis, of the {@link CombinedVibration}. Repeating
+     * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
+     * have ignored all effects.
+     */
+    private long startVibrating(
+            DeviceEffectMap effectMapping, List<Step> nextSteps) {
+        int vibratorCount = effectMapping.size();
+        if (vibratorCount == 0) {
+            // No effect was mapped to any available vibrator.
+            return 0;
+        }
+
+        AbstractVibratorStep[] steps = new AbstractVibratorStep[vibratorCount];
+        long vibrationStartTime = SystemClock.uptimeMillis();
+        for (int i = 0; i < vibratorCount; i++) {
+            steps[i] = conductor.nextVibrateStep(vibrationStartTime,
+                    conductor.getVibrators().get(effectMapping.vibratorIdAt(i)),
+                    effectMapping.effectAt(i),
+                    /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
+        }
+
+        if (steps.length == 1) {
+            // No need to prepare and trigger sync effects on a single vibrator.
+            return startVibrating(steps[0], nextSteps);
+        }
+
+        // This synchronization of vibrators should be executed one at a time, even if we are
+        // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
+        // one set of vibrators at a time.
+        // This property is guaranteed by there only being one thread (VibrationThread) executing
+        // one Step at a time, so there's no need to hold the state lock. Callbacks will be
+        // delivered asynchronously but enqueued until the step processing is finished.
+        boolean hasPrepared = false;
+        boolean hasTriggered = false;
+        long maxDuration = 0;
+        try {
+            hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
+                    effectMapping.getRequiredSyncCapabilities(),
+                    effectMapping.getVibratorIds());
+
+            for (AbstractVibratorStep step : steps) {
+                long duration = startVibrating(step, nextSteps);
+                if (duration < 0) {
+                    // One vibrator has failed, fail this entire sync attempt.
+                    return maxDuration = -1;
+                }
+                maxDuration = Math.max(maxDuration, duration);
+            }
+
+            // Check if sync was prepared and if any step was accepted by a vibrator,
+            // otherwise there is nothing to trigger here.
+            if (hasPrepared && maxDuration > 0) {
+                hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
+                        getVibration().id);
+            }
+            return maxDuration;
+        } finally {
+            if (hasPrepared && !hasTriggered) {
+                // Trigger has failed or all steps were ignored by the vibrators.
+                conductor.vibratorManagerHooks.cancelSyncedVibration();
+                nextSteps.clear();
+            } else if (maxDuration < 0) {
+                // Some vibrator failed without being prepared so other vibrators might be
+                // active. Cancel and remove every pending step from output list.
+                for (int i = nextSteps.size() - 1; i >= 0; i--) {
+                    nextSteps.remove(i).cancelImmediately();
+                }
+            }
+        }
+    }
+
+    private long startVibrating(AbstractVibratorStep step, List<Step> nextSteps) {
+        nextSteps.addAll(step.play());
+        long stepDuration = step.getVibratorOnDuration();
+        if (stepDuration < 0) {
+            // Step failed, so return negative duration to propagate failure.
+            return stepDuration;
+        }
+        // Return the longest estimation for the entire effect.
+        return Math.max(stepDuration, step.effect.getDuration());
+    }
+
+    /**
+     * Map a {@link CombinedVibration} to the vibrators available on the device.
+     *
+     * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
+     * play all of the effects in sync.
+     */
+    final class DeviceEffectMap {
+        private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
+        private final int[] mVibratorIds;
+        private final long mRequiredSyncCapabilities;
+
+        DeviceEffectMap(CombinedVibration.Mono mono) {
+            SparseArray<VibratorController> vibrators = conductor.getVibrators();
+            mVibratorEffects = new SparseArray<>(vibrators.size());
+            mVibratorIds = new int[vibrators.size()];
+            for (int i = 0; i < vibrators.size(); i++) {
+                int vibratorId = vibrators.keyAt(i);
+                VibratorInfo vibratorInfo = vibrators.valueAt(i).getVibratorInfo();
+                VibrationEffect effect = conductor.deviceEffectAdapter.apply(
+                        mono.getEffect(), vibratorInfo);
+                if (effect instanceof VibrationEffect.Composed) {
+                    mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+                    mVibratorIds[i] = vibratorId;
+                }
+            }
+            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+        }
+
+        DeviceEffectMap(CombinedVibration.Stereo stereo) {
+            SparseArray<VibratorController> vibrators = conductor.getVibrators();
+            SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
+            mVibratorEffects = new SparseArray<>();
+            for (int i = 0; i < stereoEffects.size(); i++) {
+                int vibratorId = stereoEffects.keyAt(i);
+                if (vibrators.contains(vibratorId)) {
+                    VibratorInfo vibratorInfo = vibrators.valueAt(i).getVibratorInfo();
+                    VibrationEffect effect = conductor.deviceEffectAdapter.apply(
+                            stereoEffects.valueAt(i), vibratorInfo);
+                    if (effect instanceof VibrationEffect.Composed) {
+                        mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
+                    }
+                }
+            }
+            mVibratorIds = new int[mVibratorEffects.size()];
+            for (int i = 0; i < mVibratorEffects.size(); i++) {
+                mVibratorIds[i] = mVibratorEffects.keyAt(i);
+            }
+            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
+        }
+
+        /**
+         * Return the number of vibrators mapped to play the {@link CombinedVibration} on this
+         * device.
+         */
+        public int size() {
+            return mVibratorIds.length;
+        }
+
+        /**
+         * Return all capabilities required to play the {@link CombinedVibration} in
+         * between calls to {@link IVibratorManager#prepareSynced} and
+         * {@link IVibratorManager#triggerSynced}.
+         */
+        public long getRequiredSyncCapabilities() {
+            return mRequiredSyncCapabilities;
+        }
+
+        /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */
+        public int[] getVibratorIds() {
+            return mVibratorIds;
+        }
+
+        /** Return the id of the vibrator at given index. */
+        public int vibratorIdAt(int index) {
+            return mVibratorEffects.keyAt(index);
+        }
+
+        /** Return the {@link VibrationEffect} at given index. */
+        public VibrationEffect.Composed effectAt(int index) {
+            return mVibratorEffects.valueAt(index);
+        }
+
+        /**
+         * Return all capabilities required from the {@link IVibratorManager} to prepare and
+         * trigger all given effects in sync.
+         *
+         * @return {@link IVibratorManager#CAP_SYNC} together with all required
+         * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
+         */
+        private long calculateRequiredSyncCapabilities(
+                SparseArray<VibrationEffect.Composed> effects) {
+            long prepareCap = 0;
+            for (int i = 0; i < effects.size(); i++) {
+                VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
+                if (firstSegment instanceof StepSegment) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_ON;
+                } else if (firstSegment instanceof PrebakedSegment) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
+                } else if (firstSegment instanceof PrimitiveSegment) {
+                    prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
+                }
+            }
+            int triggerCap = 0;
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
+            }
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+            }
+            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
+                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+            }
+            return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
+        }
+
+        /**
+         * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
+         * different ones, requiring a mixed trigger capability from the vibrator manager for
+         * syncing all effects.
+         */
+        private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
+            return (prepareCapabilities & capability) != 0
+                    && (prepareCapabilities & ~capability) != 0;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/Step.java b/services/core/java/com/android/server/vibrator/Step.java
new file mode 100644
index 0000000..042e8a0
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/Step.java
@@ -0,0 +1,103 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.os.CombinedVibration;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+
+import java.util.List;
+
+/**
+ * Represent a single step for playing a vibration.
+ *
+ * <p>Every step has a start time, which can be used to apply delays between steps while
+ * executing them in sequence.
+ */
+abstract class Step implements Comparable<Step> {
+    public final VibrationStepConductor conductor;
+    public final long startTime;
+
+    Step(VibrationStepConductor conductor, long startTime) {
+        this.conductor = conductor;
+        this.startTime = startTime;
+    }
+
+    protected Vibration getVibration() {
+        return conductor.getVibration();
+    }
+
+    /**
+     * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
+     * {@link CombinedVibration}.
+     */
+    public boolean isCleanUp() {
+        return false;
+    }
+
+    /** Play this step, returning a (possibly empty) list of next steps. */
+    @NonNull
+    public abstract List<Step> play();
+
+    /**
+     * Cancel this pending step and return a (possibly empty) list of clean-up steps that should
+     * be played to gracefully cancel this step.
+     */
+    @NonNull
+    public abstract List<Step> cancel();
+
+    /** Cancel this pending step immediately, skipping any clean-up. */
+    public abstract void cancelImmediately();
+
+    /**
+     * Return the duration the vibrator was turned on when this step was played.
+     *
+     * @return A positive duration that the vibrator was turned on for by this step;
+     * Zero if the segment is not supported, the step was not played yet or vibrator was never
+     * turned on by this step; A negative value if the vibrator call has failed.
+     */
+    public long getVibratorOnDuration() {
+        return 0;
+    }
+
+    /**
+     * Return true to run this step right after a vibrator has notified vibration completed,
+     * used to resume steps waiting on vibrator callbacks with a timeout.
+     */
+    public boolean acceptVibratorCompleteCallback(int vibratorId) {
+        return false;
+    }
+
+    /**
+     * Returns the time in millis to wait before playing this step. This is performed
+     * while holding the queue lock, so should not rely on potentially slow operations.
+     */
+    public long calculateWaitTime() {
+        if (startTime == Long.MAX_VALUE) {
+            // This step don't have a predefined start time, it's just marked to be executed
+            // after all other steps have finished.
+            return 0;
+        }
+        return Math.max(0, startTime - SystemClock.uptimeMillis());
+    }
+
+    @Override
+    public int compareTo(Step o) {
+        return Long.compare(startTime, o.startTime);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
new file mode 100644
index 0000000..297ef56
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.os.SystemClock;
+import android.os.Trace;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a step to turn the vibrator off.
+ *
+ * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
+ * and can be brought forward by vibrator complete callbacks. The step shouldn't be skipped, even
+ * if the vibrator-complete callback was received, as some implementations still rely on the
+ * "off" call to actually stop.
+ */
+final class TurnOffVibratorStep extends AbstractVibratorStep {
+
+    TurnOffVibratorStep(VibrationStepConductor conductor, long startTime,
+            VibratorController controller) {
+        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
+    }
+
+    @Override
+    public boolean isCleanUp() {
+        return true;
+    }
+
+    @Override
+    public List<Step> cancel() {
+        return Arrays.asList(
+                new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller));
+    }
+
+    @Override
+    public void cancelImmediately() {
+        stopVibrating();
+    }
+
+    @Override
+    public List<Step> play() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "TurnOffVibratorStep");
+        try {
+            stopVibrating();
+            return VibrationStepConductor.EMPTY_STEP_LIST;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
new file mode 100644
index 0000000..51691fb
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -0,0 +1,355 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CombinedVibration;
+import android.os.VibrationEffect;
+import android.os.WorkSource;
+import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.RampSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+/**
+ * Creates and manages a queue of steps for performing a VibrationEffect, as well as coordinating
+ * dispatch of callbacks.
+ */
+final class VibrationStepConductor {
+    /**
+     * Extra timeout added to the end of each vibration step to ensure it finishes even when
+     * vibrator callbacks are lost.
+     */
+    static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
+    /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
+    static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
+    static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
+
+    final Object mLock = new Object();
+
+    // Used within steps.
+    public final VibrationSettings vibrationSettings;
+    public final DeviceVibrationEffectAdapter deviceEffectAdapter;
+    public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
+
+    private final WorkSource mWorkSource;
+    private final Vibration mVibration;
+    private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
+    @GuardedBy("mLock")
+    private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
+    @GuardedBy("mLock")
+    private final Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
+
+    @GuardedBy("mLock")
+    private int mPendingVibrateSteps;
+    @GuardedBy("mLock")
+    private int mConsumedStartVibrateSteps;
+    @GuardedBy("mLock")
+    private int mSuccessfulVibratorOnSteps;
+    @GuardedBy("mLock")
+    private boolean mWaitToProcessVibratorCompleteCallbacks;
+
+    VibrationStepConductor(Vibration vib, VibrationSettings vibrationSettings,
+            DeviceVibrationEffectAdapter effectAdapter,
+            SparseArray<VibratorController> availableVibrators,
+            VibrationThread.VibratorManagerHooks vibratorManagerHooks) {
+        this.mVibration = vib;
+        this.vibrationSettings = vibrationSettings;
+        this.deviceEffectAdapter = effectAdapter;
+        this.vibratorManagerHooks = vibratorManagerHooks;
+        this.mWorkSource = new WorkSource(mVibration.uid);
+
+        CombinedVibration effect = vib.getEffect();
+        for (int i = 0; i < availableVibrators.size(); i++) {
+            if (effect.hasVibrator(availableVibrators.keyAt(i))) {
+                mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
+            }
+        }
+    }
+
+    @Nullable
+    AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
+            VibrationEffect.Composed effect, int segmentIndex,
+            long previousStepVibratorOffTimeout) {
+        if (segmentIndex >= effect.getSegments().size()) {
+            segmentIndex = effect.getRepeatIndex();
+        }
+        if (segmentIndex < 0) {
+            // No more segments to play, last step is to complete the vibration on this vibrator.
+            return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false,
+                    controller, previousStepVibratorOffTimeout);
+        }
+
+        VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
+        if (segment instanceof PrebakedSegment) {
+            return new PerformPrebakedVibratorStep(this, startTime, controller, effect,
+                    segmentIndex, previousStepVibratorOffTimeout);
+        }
+        if (segment instanceof PrimitiveSegment) {
+            return new ComposePrimitivesVibratorStep(this, startTime, controller, effect,
+                    segmentIndex, previousStepVibratorOffTimeout);
+        }
+        if (segment instanceof RampSegment) {
+            return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
+                    previousStepVibratorOffTimeout);
+        }
+        return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
+                previousStepVibratorOffTimeout);
+    }
+
+    public void initializeForEffect(@NonNull CombinedVibration.Sequential vibration) {
+        synchronized (mLock) {
+            mPendingVibrateSteps++;
+            mNextSteps.offer(new StartSequentialEffectStep(this, vibration));
+        }
+    }
+
+    public Vibration getVibration() {
+        return mVibration;
+    }
+
+    public WorkSource getWorkSource() {
+        return mWorkSource;
+    }
+
+    SparseArray<VibratorController> getVibrators() {
+        return mVibrators;
+    }
+
+    public boolean isFinished() {
+        synchronized (mLock) {
+            return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
+        }
+    }
+
+    /**
+     * Calculate the {@link Vibration.Status} based on the current queue state and the expected
+     * number of {@link StartSequentialEffectStep} to be played.
+     */
+    public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
+        synchronized (mLock) {
+            if (mPendingVibrateSteps > 0
+                    || mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
+                return Vibration.Status.RUNNING;
+            }
+            if (mSuccessfulVibratorOnSteps > 0) {
+                return Vibration.Status.FINISHED;
+            }
+            // If no step was able to turn the vibrator ON successfully.
+            return Vibration.Status.IGNORED_UNSUPPORTED;
+        }
+    }
+
+    /** Returns the time in millis to wait before calling {@link #runNextStep()}. */
+    @GuardedBy("mLock")
+    public long getWaitMillisBeforeNextStepLocked() {
+        if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+            // Steps resumed by vibrator complete callback should be played right away.
+            return 0;
+        }
+        Step nextStep = mNextSteps.peek();
+        return nextStep == null ? 0 : nextStep.calculateWaitTime();
+    }
+
+    /**
+     * Play and remove the step at the top of this queue, and also adds the next steps generated
+     * to be played next.
+     */
+    public void runNextStep() {
+        // Vibrator callbacks should wait until the polled step is played and the next steps are
+        // added back to the queue, so they can handle the callback.
+        markWaitToProcessVibratorCallbacks();
+        try {
+            Step nextStep = pollNext();
+            if (nextStep != null) {
+                // This might turn on the vibrator and have a HAL latency. Execute this outside
+                // any lock to avoid blocking other interactions with the thread.
+                List<Step> nextSteps = nextStep.play();
+                synchronized (mLock) {
+                    if (nextStep.getVibratorOnDuration() > 0) {
+                        mSuccessfulVibratorOnSteps++;
+                    }
+                    if (nextStep instanceof StartSequentialEffectStep) {
+                        mConsumedStartVibrateSteps++;
+                    }
+                    if (!nextStep.isCleanUp()) {
+                        mPendingVibrateSteps--;
+                    }
+                    for (int i = 0; i < nextSteps.size(); i++) {
+                        mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
+                    }
+                    mNextSteps.addAll(nextSteps);
+                }
+            }
+        } finally {
+            synchronized (mLock) {
+                processVibratorCompleteCallbacksLocked();
+            }
+        }
+    }
+
+    /**
+     * Notify the vibrator completion.
+     *
+     * <p>This is a lightweight method that do not trigger any operation from {@link
+     * VibratorController}, so it can be called directly from a native callback.
+     */
+    @GuardedBy("mLock")
+    private void notifyVibratorCompleteLocked(int vibratorId) {
+        mCompletionNotifiedVibrators.offer(vibratorId);
+        if (!mWaitToProcessVibratorCompleteCallbacks) {
+            // No step is being played or cancelled now, process the callback right away.
+            processVibratorCompleteCallbacksLocked();
+        }
+    }
+
+    public void notifyVibratorComplete(int vibratorId) {
+        synchronized (mLock) {
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "Vibration complete reported by vibrator " + vibratorId);
+            }
+            notifyVibratorCompleteLocked(vibratorId);
+            mLock.notify();
+        }
+    }
+
+    public void notifySyncedVibrationComplete() {
+        synchronized (mLock) {
+            if (VibrationThread.DEBUG) {
+                Slog.d(VibrationThread.TAG,
+                        "Synced vibration complete reported by vibrator manager");
+            }
+            for (int i = 0; i < mVibrators.size(); i++) {
+                notifyVibratorCompleteLocked(mVibrators.keyAt(i));
+            }
+            mLock.notify();
+        }
+    }
+
+    /**
+     * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
+     *
+     * <p>This will remove all steps and replace them with respective
+     * {@link Step#cancel()}.
+     */
+    public void cancel() {
+        // Vibrator callbacks should wait until all steps from the queue are properly cancelled
+        // and clean up steps are added back to the queue, so they can handle the callback.
+        markWaitToProcessVibratorCallbacks();
+        try {
+            List<Step> cleanUpSteps = new ArrayList<>();
+            Step step;
+            while ((step = pollNext()) != null) {
+                cleanUpSteps.addAll(step.cancel());
+            }
+            synchronized (mLock) {
+                // All steps generated by Step.cancel() should be clean-up steps.
+                mPendingVibrateSteps = 0;
+                mNextSteps.addAll(cleanUpSteps);
+            }
+        } finally {
+            synchronized (mLock) {
+                processVibratorCompleteCallbacksLocked();
+            }
+        }
+    }
+
+    /**
+     * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
+     *
+     * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
+     */
+    public void cancelImmediately() {
+        // Vibrator callbacks should wait until all steps from the queue are properly cancelled.
+        markWaitToProcessVibratorCallbacks();
+        try {
+            Step step;
+            while ((step = pollNext()) != null) {
+                // This might turn off the vibrator and have a HAL latency. Execute this outside
+                // any lock to avoid blocking other interactions with the thread.
+                step.cancelImmediately();
+            }
+            synchronized (mLock) {
+                mPendingVibrateSteps = 0;
+            }
+        } finally {
+            synchronized (mLock) {
+                processVibratorCompleteCallbacksLocked();
+            }
+        }
+    }
+
+    @Nullable
+    private Step pollNext() {
+        synchronized (mLock) {
+            // Prioritize the steps resumed by a vibrator complete callback.
+            if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+                return mPendingOnVibratorCompleteSteps.poll();
+            }
+            return mNextSteps.poll();
+        }
+    }
+
+    private void markWaitToProcessVibratorCallbacks() {
+        synchronized (mLock) {
+            mWaitToProcessVibratorCompleteCallbacks = true;
+        }
+    }
+
+    /**
+     * Notify the step in this queue that should be resumed by the vibrator completion
+     * callback and keep it separate to be consumed by {@link #runNextStep()}.
+     *
+     * <p>This is a lightweight method that do not trigger any operation from {@link
+     * VibratorController}, so it can be called directly from a native callback.
+     *
+     * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
+     * first step found will be resumed by this method, in no particular order.
+     */
+    @GuardedBy("mLock")
+    private void processVibratorCompleteCallbacksLocked() {
+        mWaitToProcessVibratorCompleteCallbacks = false;
+        while (!mCompletionNotifiedVibrators.isEmpty()) {
+            int vibratorId = mCompletionNotifiedVibrators.poll();
+            Iterator<Step> it = mNextSteps.iterator();
+            while (it.hasNext()) {
+                Step step = it.next();
+                if (step.acceptVibratorCompleteCallback(vibratorId)) {
+                    it.remove();
+                    mPendingOnVibratorCompleteSteps.offer(step);
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index fdd9913..f2cd8c3 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,98 +16,75 @@
 
 package com.android.server.vibrator;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.hardware.vibrator.IVibratorManager;
 import android.os.CombinedVibration;
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.Trace;
-import android.os.VibrationEffect;
-import android.os.VibratorInfo;
-import android.os.WorkSource;
-import android.os.vibrator.PrebakedSegment;
-import android.os.vibrator.PrimitiveSegment;
-import android.os.vibrator.RampSegment;
-import android.os.vibrator.StepSegment;
-import android.os.vibrator.VibrationEffectSegment;
 import android.util.Slog;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.FrameworkStatsLog;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.PriorityQueue;
-import java.util.Queue;
 
 /** Plays a {@link Vibration} in dedicated thread. */
 final class VibrationThread extends Thread implements IBinder.DeathRecipient {
-    private static final String TAG = "VibrationThread";
-    private static final boolean DEBUG = false;
+    static final String TAG = "VibrationThread";
+    static final boolean DEBUG = false;
 
-    /**
-     * Extra timeout added to the end of each vibration step to ensure it finishes even when
-     * vibrator callbacks are lost.
-     */
-    private static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
-
-    /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
-    private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
-
-    /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
-    private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
-
-    private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
-
-    /** Callbacks for playing a {@link Vibration}. */
-    interface VibrationCallbacks {
+    /** Calls into VibratorManager functionality needed for playing a {@link Vibration}. */
+    interface VibratorManagerHooks {
 
         /**
-         * Callback triggered before starting a synchronized vibration step. This will be called
-         * with {@code requiredCapabilities = 0} if no synchronization is required.
+         * Request the manager to prepare for triggering a synchronized vibration step.
          *
          * @param requiredCapabilities The required syncing capabilities for this preparation step.
-         *                             Expects a combination of values from
+         *                             Expect CAP_SYNC and a combination of values from
          *                             IVibratorManager.CAP_PREPARE_* and
          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
          * @param vibratorIds          The id of the vibrators to be prepared.
          */
         boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
 
-        /** Callback triggered after synchronized vibrations were prepared. */
+        /**
+         * Request the manager to trigger a synchronized vibration. The vibration must already
+         * have been prepared with {@link #prepareSyncedVibration}.
+         */
         boolean triggerSyncedVibration(long vibrationId);
 
-        /** Callback triggered to cancel a prepared synced vibration. */
+        /** Tell the manager to cancel a synced vibration. */
         void cancelSyncedVibration();
 
-        /** Callback triggered when the vibration is complete. */
+        /**
+         * Record that a vibrator was turned on, and may remain on for the specified duration,
+         * on behalf of the given uid.
+         */
+        void noteVibratorOn(int uid, long duration);
+
+        /** Record that a vibrator was turned off, on behalf of the given uid. */
+        void noteVibratorOff(int uid);
+
+        /**
+         * Tell the manager that the currently active vibration has completed its vibration, from
+         * the perspective of the Effect. However, the VibrationThread may still be continuing with
+         * cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
+         * is called.
+         */
         void onVibrationCompleted(long vibrationId, Vibration.Status status);
 
-        /** Callback triggered when the vibrators are released after the thread is complete. */
-        void onVibratorsReleased();
+        /**
+         * Tells the manager that the VibrationThread is finished with the previous vibration and
+         * all of its cleanup tasks, and the vibrators can now be used for another vibration.
+         */
+        void onVibrationThreadReleased();
     }
 
-    private final Object mLock = new Object();
-    private final WorkSource mWorkSource;
     private final PowerManager.WakeLock mWakeLock;
-    private final IBatteryStats mBatteryStatsService;
-    private final VibrationSettings mVibrationSettings;
-    private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
-    private final Vibration mVibration;
-    private final VibrationCallbacks mCallbacks;
-    private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
-    private final StepQueue mStepQueue = new StepQueue();
+    private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
+
+    private final VibrationStepConductor mStepConductor;
 
     private volatile boolean mStop;
     private volatile boolean mForceStop;
@@ -117,30 +94,20 @@
     VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
             DeviceVibrationEffectAdapter effectAdapter,
             SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
-            IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
-        mVibration = vib;
-        mVibrationSettings = vibrationSettings;
-        mDeviceEffectAdapter = effectAdapter;
-        mCallbacks = callbacks;
-        mWorkSource = new WorkSource(mVibration.uid);
+            VibratorManagerHooks vibratorManagerHooks) {
+        mVibratorManagerHooks = vibratorManagerHooks;
         mWakeLock = wakeLock;
-        mBatteryStatsService = batteryStatsService;
-
-        CombinedVibration effect = vib.getEffect();
-        for (int i = 0; i < availableVibrators.size(); i++) {
-            if (effect.hasVibrator(availableVibrators.keyAt(i))) {
-                mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
-            }
-        }
+        mStepConductor = new VibrationStepConductor(vib, vibrationSettings, effectAdapter,
+                availableVibrators, vibratorManagerHooks);
     }
 
     Vibration getVibration() {
-        return mVibration;
+        return mStepConductor.getVibration();
     }
 
     @VisibleForTesting
     SparseArray<VibratorController> getVibrators() {
-        return mVibrators;
+        return mStepConductor.getVibrators();
     }
 
     @Override
@@ -163,13 +130,13 @@
                 clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
             }
         } finally {
-            mCallbacks.onVibratorsReleased();
+            mVibratorManagerHooks.onVibrationThreadReleased();
         }
     }
 
     /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
     private void runWithWakeLock() {
-        mWakeLock.setWorkSource(mWorkSource);
+        mWakeLock.setWorkSource(mStepConductor.getWorkSource());
         mWakeLock.acquire();
         try {
             runWithWakeLockAndDeathLink();
@@ -183,8 +150,9 @@
      * Called from within runWithWakeLock.
      */
     private void runWithWakeLockAndDeathLink() {
+        IBinder vibrationBinderToken = mStepConductor.getVibration().token;
         try {
-            mVibration.token.linkToDeath(this, 0);
+            vibrationBinderToken.linkToDeath(this, 0);
         } catch (RemoteException e) {
             Slog.e(TAG, "Error linking vibration to token death", e);
             clientVibrationCompleteIfNotAlready(Vibration.Status.IGNORED_ERROR_TOKEN);
@@ -196,7 +164,7 @@
             playVibration();
         } finally {
             try {
-                mVibration.token.unlinkToDeath(this, 0);
+                vibrationBinderToken.unlinkToDeath(this, 0);
             } catch (NoSuchElementException e) {
                 Slog.wtf(TAG, "Failed to unlink token", e);
             }
@@ -210,11 +178,11 @@
             return;
         }
         mStop = true;
-        synchronized (mLock) {
+        synchronized (mStepConductor.mLock) {
             if (DEBUG) {
                 Slog.d(TAG, "Vibration cancelled");
             }
-            mLock.notify();
+            mStepConductor.mLock.notify();
         }
     }
 
@@ -225,36 +193,22 @@
             return;
         }
         mStop = mForceStop = true;
-        synchronized (mLock) {
+        synchronized (mStepConductor.mLock) {
             if (DEBUG) {
                 Slog.d(TAG, "Vibration cancelled immediately");
             }
-            mLock.notify();
+            mStepConductor.mLock.notify();
         }
     }
 
     /** Notify current vibration that a synced step has completed. */
     public void syncedVibrationComplete() {
-        synchronized (mLock) {
-            if (DEBUG) {
-                Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
-            }
-            for (int i = 0; i < mVibrators.size(); i++) {
-                mStepQueue.notifyVibratorComplete(mVibrators.keyAt(i));
-            }
-            mLock.notify();
-        }
+        mStepConductor.notifySyncedVibrationComplete();
     }
 
     /** Notify current vibration that a step has completed on given vibrator. */
     public void vibratorComplete(int vibratorId) {
-        synchronized (mLock) {
-            if (DEBUG) {
-                Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
-            }
-            mStepQueue.notifyVibratorComplete(vibratorId);
-            mLock.notify();
-        }
+        mStepConductor.notifyVibratorComplete(vibratorId);
     }
 
     // Indicate that the vibration is complete. This can be called multiple times only for
@@ -263,48 +217,55 @@
     private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) {
         if (!mCalledVibrationCompleteCallback) {
             mCalledVibrationCompleteCallback = true;
-            mCallbacks.onVibrationCompleted(mVibration.id, completedStatus);
+            mVibratorManagerHooks.onVibrationCompleted(
+                    mStepConductor.getVibration().id, completedStatus);
         }
     }
 
     private void playVibration() {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
         try {
-            CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
+            CombinedVibration.Sequential sequentialEffect =
+                    toSequential(mStepConductor.getVibration().getEffect());
             final int sequentialEffectSize = sequentialEffect.getEffects().size();
-            mStepQueue.offer(new StartVibrateStep(sequentialEffect));
+            mStepConductor.initializeForEffect(sequentialEffect);
 
-            while (!mStepQueue.isEmpty()) {
-                long waitTime;
-                synchronized (mLock) {
-                    waitTime = mStepQueue.calculateWaitTime();
-                    if (waitTime > 0) {
+            while (!mStepConductor.isFinished()) {
+                long waitMillisBeforeNextStep;
+                synchronized (mStepConductor.mLock) {
+                    waitMillisBeforeNextStep = mStepConductor.getWaitMillisBeforeNextStepLocked();
+                    if (waitMillisBeforeNextStep > 0) {
                         try {
-                            mLock.wait(waitTime);
+                            mStepConductor.mLock.wait(waitMillisBeforeNextStep);
                         } catch (InterruptedException e) {
                         }
                     }
                 }
-                // If we waited, the queue may have changed, so let the loop run again.
-                if (waitTime <= 0) {
+                // Only run the next vibration step if we didn't have to wait in this loop.
+                // If we waited then the queue may have changed or the wait could have been
+                // interrupted by a cancel call, so loop again to re-evaluate the scheduling of
+                // the queue top element.
+                if (waitMillisBeforeNextStep <= 0) {
                     if (DEBUG) {
                         Slog.d(TAG, "Play vibration consuming next step...");
                     }
-                    mStepQueue.consumeNext();
+                    // Run the step without holding the main lock, to avoid HAL interactions from
+                    // blocking the thread.
+                    mStepConductor.runNextStep();
                 }
                 Vibration.Status status = mStop ? Vibration.Status.CANCELLED
-                        : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
+                        : mStepConductor.calculateVibrationStatus(sequentialEffectSize);
                 if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
                     // First time vibration stopped running, start clean-up tasks and notify
                     // callback immediately.
                     clientVibrationCompleteIfNotAlready(status);
                     if (status == Vibration.Status.CANCELLED) {
-                        mStepQueue.cancel();
+                        mStepConductor.cancel();
                     }
                 }
                 if (mForceStop) {
                     // Cancel every step and stop playing them right away, even clean-up steps.
-                    mStepQueue.cancelImmediately();
+                    mStepConductor.cancelImmediately();
                     clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
                     break;
                 }
@@ -314,61 +275,6 @@
         }
     }
 
-    private void noteVibratorOn(long duration) {
-        try {
-            if (duration <= 0) {
-                return;
-            }
-            if (duration == Long.MAX_VALUE) {
-                // Repeating duration has started. Report a fixed duration here, noteVibratorOff
-                // should be called when this is cancelled.
-                duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
-            }
-            mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
-                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
-                    duration);
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void noteVibratorOff() {
-        try {
-            mBatteryStatsService.noteVibratorOff(mVibration.uid);
-            FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
-                    mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
-                    /* duration= */ 0);
-        } catch (RemoteException e) {
-        }
-    }
-
-    @Nullable
-    private SingleVibratorStep nextVibrateStep(long startTime, VibratorController controller,
-            VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout) {
-        if (segmentIndex >= effect.getSegments().size()) {
-            segmentIndex = effect.getRepeatIndex();
-        }
-        if (segmentIndex < 0) {
-            // No more segments to play, last step is to complete the vibration on this vibrator.
-            return new CompleteStep(startTime, /* cancelled= */ false, controller,
-                    vibratorOffTimeout);
-        }
-
-        VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
-        if (segment instanceof PrebakedSegment) {
-            return new PerformStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
-        }
-        if (segment instanceof PrimitiveSegment) {
-            return new ComposePrimitivesStep(startTime, controller, effect, segmentIndex,
-                    vibratorOffTimeout);
-        }
-        if (segment instanceof RampSegment) {
-            return new ComposePwleStep(startTime, controller, effect, segmentIndex,
-                    vibratorOffTimeout);
-        }
-        return new AmplitudeStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
-    }
-
     private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
         if (effect instanceof CombinedVibration.Sequential) {
             return (CombinedVibration.Sequential) effect;
@@ -378,1269 +284,4 @@
                 .combine();
     }
 
-    /** Queue for {@link Step Steps}, sorted by their start time. */
-    private final class StepQueue {
-        @GuardedBy("mLock")
-        private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
-        @GuardedBy("mLock")
-        private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
-        @GuardedBy("mLock")
-        private final Queue<Integer> mNotifiedVibrators = new LinkedList<>();
-
-        @GuardedBy("mLock")
-        private int mPendingVibrateSteps;
-        @GuardedBy("mLock")
-        private int mConsumedStartVibrateSteps;
-        @GuardedBy("mLock")
-        private int mSuccessfulVibratorOnSteps;
-        @GuardedBy("mLock")
-        private boolean mWaitToProcessVibratorCallbacks;
-
-        public void offer(@NonNull Step step) {
-            synchronized (mLock) {
-                if (!step.isCleanUp()) {
-                    mPendingVibrateSteps++;
-                }
-                mNextSteps.offer(step);
-            }
-        }
-
-        public boolean isEmpty() {
-            synchronized (mLock) {
-                return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
-            }
-        }
-
-        /**
-         * Calculate the {@link Vibration.Status} based on the current queue state and the expected
-         * number of {@link StartVibrateStep} to be played.
-         */
-        public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
-            synchronized (mLock) {
-                if (mPendingVibrateSteps > 0
-                        || mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
-                    return Vibration.Status.RUNNING;
-                }
-                if (mSuccessfulVibratorOnSteps > 0) {
-                    return Vibration.Status.FINISHED;
-                }
-                // If no step was able to turn the vibrator ON successfully.
-                return Vibration.Status.IGNORED_UNSUPPORTED;
-            }
-        }
-
-        /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
-        @GuardedBy("mLock")
-        public long calculateWaitTime() {
-            if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
-                // Steps anticipated by vibrator complete callback should be played right away.
-                return 0;
-            }
-            Step nextStep = mNextSteps.peek();
-            return nextStep == null ? 0 : nextStep.calculateWaitTime();
-        }
-
-        /**
-         * Play and remove the step at the top of this queue, and also adds the next steps generated
-         * to be played next.
-         */
-        public void consumeNext() {
-            // Vibrator callbacks should wait until the polled step is played and the next steps are
-            // added back to the queue, so they can handle the callback.
-            markWaitToProcessVibratorCallbacks();
-            try {
-                Step nextStep = pollNext();
-                if (nextStep != null) {
-                    // This might turn on the vibrator and have a HAL latency. Execute this outside
-                    // any lock to avoid blocking other interactions with the thread.
-                    List<Step> nextSteps = nextStep.play();
-                    synchronized (mLock) {
-                        if (nextStep.getVibratorOnDuration() > 0) {
-                            mSuccessfulVibratorOnSteps++;
-                        }
-                        if (nextStep instanceof StartVibrateStep) {
-                            mConsumedStartVibrateSteps++;
-                        }
-                        if (!nextStep.isCleanUp()) {
-                            mPendingVibrateSteps--;
-                        }
-                        for (int i = 0; i < nextSteps.size(); i++) {
-                            mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
-                        }
-                        mNextSteps.addAll(nextSteps);
-                    }
-                }
-            } finally {
-                synchronized (mLock) {
-                    processVibratorCallbacks();
-                }
-            }
-        }
-
-        /**
-         * Notify the vibrator completion.
-         *
-         * <p>This is a lightweight method that do not trigger any operation from {@link
-         * VibratorController}, so it can be called directly from a native callback.
-         */
-        @GuardedBy("mLock")
-        public void notifyVibratorComplete(int vibratorId) {
-            mNotifiedVibrators.offer(vibratorId);
-            if (!mWaitToProcessVibratorCallbacks) {
-                // No step is being played or cancelled now, process the callback right away.
-                processVibratorCallbacks();
-            }
-        }
-
-        /**
-         * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
-         *
-         * <p>This will remove all steps and replace them with respective
-         * {@link Step#cancel()}.
-         */
-        public void cancel() {
-            // Vibrator callbacks should wait until all steps from the queue are properly cancelled
-            // and clean up steps are added back to the queue, so they can handle the callback.
-            markWaitToProcessVibratorCallbacks();
-            try {
-                List<Step> cleanUpSteps = new ArrayList<>();
-                Step step;
-                while ((step = pollNext()) != null) {
-                    cleanUpSteps.addAll(step.cancel());
-                }
-                synchronized (mLock) {
-                    // All steps generated by Step.cancel() should be clean-up steps.
-                    mPendingVibrateSteps = 0;
-                    mNextSteps.addAll(cleanUpSteps);
-                }
-            } finally {
-                synchronized (mLock) {
-                    processVibratorCallbacks();
-                }
-            }
-        }
-
-        /**
-         * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
-         *
-         * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
-         */
-        public void cancelImmediately() {
-            // Vibrator callbacks should wait until all steps from the queue are properly cancelled.
-            markWaitToProcessVibratorCallbacks();
-            try {
-                Step step;
-                while ((step = pollNext()) != null) {
-                    // This might turn off the vibrator and have a HAL latency. Execute this outside
-                    // any lock to avoid blocking other interactions with the thread.
-                    step.cancelImmediately();
-                }
-                synchronized (mLock) {
-                    mPendingVibrateSteps = 0;
-                }
-            } finally {
-                synchronized (mLock) {
-                    processVibratorCallbacks();
-                }
-            }
-        }
-
-        @Nullable
-        private Step pollNext() {
-            synchronized (mLock) {
-                // Prioritize the steps anticipated by a vibrator complete callback.
-                if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
-                    return mPendingOnVibratorCompleteSteps.poll();
-                }
-                return mNextSteps.poll();
-            }
-        }
-
-        private void markWaitToProcessVibratorCallbacks() {
-            synchronized (mLock) {
-                mWaitToProcessVibratorCallbacks = true;
-            }
-        }
-
-        /**
-         * Notify the step in this queue that should be anticipated by the vibrator completion
-         * callback and keep it separate to be consumed by {@link #consumeNext()}.
-         *
-         * <p>This is a lightweight method that do not trigger any operation from {@link
-         * VibratorController}, so it can be called directly from a native callback.
-         *
-         * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
-         * first step found will be anticipated by this method, in no particular order.
-         */
-        @GuardedBy("mLock")
-        private void processVibratorCallbacks() {
-            mWaitToProcessVibratorCallbacks = false;
-            while (!mNotifiedVibrators.isEmpty()) {
-                int vibratorId = mNotifiedVibrators.poll();
-                Iterator<Step> it = mNextSteps.iterator();
-                while (it.hasNext()) {
-                    Step step = it.next();
-                    if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
-                        it.remove();
-                        mPendingOnVibratorCompleteSteps.offer(step);
-                        break;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Represent a single step for playing a vibration.
-     *
-     * <p>Every step has a start time, which can be used to apply delays between steps while
-     * executing them in sequence.
-     */
-    private abstract class Step implements Comparable<Step> {
-        public final long startTime;
-
-        Step(long startTime) {
-            this.startTime = startTime;
-        }
-
-        /**
-         * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
-         * {@link CombinedVibration}.
-         */
-        public boolean isCleanUp() {
-            return false;
-        }
-
-        /** Play this step, returning a (possibly empty) list of next steps. */
-        @NonNull
-        public abstract List<Step> play();
-
-        /**
-         * Cancel this pending step and return a (possibly empty) list of clean-up steps that should
-         * be played to gracefully cancel this step.
-         */
-        @NonNull
-        public abstract List<Step> cancel();
-
-        /** Cancel this pending step immediately, skipping any clean-up. */
-        public abstract void cancelImmediately();
-
-        /**
-         * Return the duration the vibrator was turned on when this step was played.
-         *
-         * @return A positive duration that the vibrator was turned on for by this step;
-         * Zero if the segment is not supported, the step was not played yet or vibrator was never
-         * turned on by this step; A negative value if the vibrator call has failed.
-         */
-        public long getVibratorOnDuration() {
-            return 0;
-        }
-
-        /**
-         * Return true to play this step right after a vibrator has notified vibration completed,
-         * used to anticipate steps waiting on vibrator callbacks with a timeout.
-         */
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
-            return false;
-        }
-
-        /**
-         * Returns the time in millis to wait before playing this step. This is performed
-         * while holding the queue lock, so should not rely on potentially slow operations.
-         */
-        public long calculateWaitTime() {
-            if (startTime == Long.MAX_VALUE) {
-                // This step don't have a predefined start time, it's just marked to be executed
-                // after all other steps have finished.
-                return 0;
-            }
-            return Math.max(0, startTime - SystemClock.uptimeMillis());
-        }
-
-        @Override
-        public int compareTo(Step o) {
-            return Long.compare(startTime, o.startTime);
-        }
-    }
-
-    /**
-     * Starts a sync vibration.
-     *
-     * <p>If this step has successfully started playing a vibration on any vibrator, it will always
-     * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
-     * all their individual steps.
-     *
-     * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
-     * sequential effect isn't finished yet.
-     */
-    private final class StartVibrateStep extends Step {
-        public final CombinedVibration.Sequential sequentialEffect;
-        public final int currentIndex;
-
-        private long mVibratorsOnMaxDuration;
-
-        StartVibrateStep(CombinedVibration.Sequential effect) {
-            this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
-        }
-
-        StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index) {
-            super(startTime);
-            sequentialEffect = effect;
-            currentIndex = index;
-        }
-
-        @Override
-        public long getVibratorOnDuration() {
-            return mVibratorsOnMaxDuration;
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
-            List<Step> nextSteps = new ArrayList<>();
-            mVibratorsOnMaxDuration = -1;
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
-                }
-                CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex);
-                DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
-                if (effectMapping == null) {
-                    // Unable to map effects to vibrators, ignore this step.
-                    return nextSteps;
-                }
-
-                mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
-                noteVibratorOn(mVibratorsOnMaxDuration);
-            } finally {
-                if (mVibratorsOnMaxDuration >= 0) {
-                    // It least one vibrator was started then add a finish step to wait for all
-                    // active vibrators to finish their individual steps before going to the next.
-                    // Otherwise this step was ignored so just go to the next one.
-                    Step nextStep =
-                            mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep();
-                    if (nextStep != null) {
-                        nextSteps.add(nextStep);
-                    }
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-            return nextSteps;
-        }
-
-        @Override
-        public List<Step> cancel() {
-            return EMPTY_STEP_LIST;
-        }
-
-        @Override
-        public void cancelImmediately() {
-        }
-
-        /**
-         * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
-         * time this method is called, or null if sequence is complete.
-         */
-        @Nullable
-        private Step nextStep() {
-            int nextIndex = currentIndex + 1;
-            if (nextIndex >= sequentialEffect.getEffects().size()) {
-                return null;
-            }
-            long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
-            long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
-            return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
-        }
-
-        /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
-        @Nullable
-        private DeviceEffectMap createEffectToVibratorMapping(
-                CombinedVibration effect) {
-            if (effect instanceof CombinedVibration.Mono) {
-                return new DeviceEffectMap((CombinedVibration.Mono) effect);
-            }
-            if (effect instanceof CombinedVibration.Stereo) {
-                return new DeviceEffectMap((CombinedVibration.Stereo) effect);
-            }
-            return null;
-        }
-
-        /**
-         * Starts playing effects on designated vibrators, in sync.
-         *
-         * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators
-         * @param nextSteps     An output list to accumulate the future {@link Step Steps} created
-         *                      by this method, typically one for each vibrator that has
-         *                      successfully started vibrating on this step.
-         * @return The duration, in millis, of the {@link CombinedVibration}. Repeating
-         * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
-         * have ignored all effects.
-         */
-        private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
-            int vibratorCount = effectMapping.size();
-            if (vibratorCount == 0) {
-                // No effect was mapped to any available vibrator.
-                return 0;
-            }
-
-            SingleVibratorStep[] steps = new SingleVibratorStep[vibratorCount];
-            long vibrationStartTime = SystemClock.uptimeMillis();
-            for (int i = 0; i < vibratorCount; i++) {
-                steps[i] = nextVibrateStep(vibrationStartTime,
-                        mVibrators.get(effectMapping.vibratorIdAt(i)),
-                        effectMapping.effectAt(i),
-                        /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
-            }
-
-            if (steps.length == 1) {
-                // No need to prepare and trigger sync effects on a single vibrator.
-                return startVibrating(steps[0], nextSteps);
-            }
-
-            // This synchronization of vibrators should be executed one at a time, even if we are
-            // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
-            // one set of vibrators at a time.
-            synchronized (mLock) {
-                boolean hasPrepared = false;
-                boolean hasTriggered = false;
-                long maxDuration = 0;
-                try {
-                    hasPrepared = mCallbacks.prepareSyncedVibration(
-                            effectMapping.getRequiredSyncCapabilities(),
-                            effectMapping.getVibratorIds());
-
-                    for (SingleVibratorStep step : steps) {
-                        long duration = startVibrating(step, nextSteps);
-                        if (duration < 0) {
-                            // One vibrator has failed, fail this entire sync attempt.
-                            return maxDuration = -1;
-                        }
-                        maxDuration = Math.max(maxDuration, duration);
-                    }
-
-                    // Check if sync was prepared and if any step was accepted by a vibrator,
-                    // otherwise there is nothing to trigger here.
-                    if (hasPrepared && maxDuration > 0) {
-                        hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
-                    }
-                    return maxDuration;
-                } finally {
-                    if (hasPrepared && !hasTriggered) {
-                        // Trigger has failed or all steps were ignored by the vibrators.
-                        mCallbacks.cancelSyncedVibration();
-                        nextSteps.clear();
-                    } else if (maxDuration < 0) {
-                        // Some vibrator failed without being prepared so other vibrators might be
-                        // active. Cancel and remove every pending step from output list.
-                        for (int i = nextSteps.size() - 1; i >= 0; i--) {
-                            nextSteps.remove(i).cancelImmediately();
-                        }
-                    }
-                }
-            }
-        }
-
-        private long startVibrating(SingleVibratorStep step, List<Step> nextSteps) {
-            nextSteps.addAll(step.play());
-            long stepDuration = step.getVibratorOnDuration();
-            if (stepDuration < 0) {
-                // Step failed, so return negative duration to propagate failure.
-                return stepDuration;
-            }
-            // Return the longest estimation for the entire effect.
-            return Math.max(stepDuration, step.effect.getDuration());
-        }
-    }
-
-    /**
-     * Finish a sync vibration started by a {@link StartVibrateStep}.
-     *
-     * <p>This only plays after all active vibrators steps have finished, and adds a {@link
-     * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
-     */
-    private final class FinishVibrateStep extends Step {
-        public final StartVibrateStep startedStep;
-
-        FinishVibrateStep(StartVibrateStep startedStep) {
-            super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
-            this.startedStep = startedStep;
-        }
-
-        @Override
-        public boolean isCleanUp() {
-            // This step only notes that all the vibrators has been turned off.
-            return true;
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
-                }
-                noteVibratorOff();
-                Step nextStep = startedStep.nextStep();
-                return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-
-        @Override
-        public List<Step> cancel() {
-            cancelImmediately();
-            return EMPTY_STEP_LIST;
-        }
-
-        @Override
-        public void cancelImmediately() {
-            noteVibratorOff();
-        }
-    }
-
-    /**
-     * Represent a step on a single vibrator that plays one or more segments from a
-     * {@link VibrationEffect.Composed} effect.
-     */
-    private abstract class SingleVibratorStep extends Step {
-        public final VibratorController controller;
-        public final VibrationEffect.Composed effect;
-        public final int segmentIndex;
-        public final long vibratorOffTimeout;
-
-        long mVibratorOnResult;
-        boolean mVibratorCallbackReceived;
-
-        /**
-         * @param startTime          The time to schedule this step in the {@link StepQueue}.
-         * @param controller         The vibrator that is playing the effect.
-         * @param effect             The effect being played in this step.
-         * @param index              The index of the next segment to be played by this step
-         * @param vibratorOffTimeout The time the vibrator is expected to complete any previous
-         *                           vibration and turn off. This is used to allow this step to be
-         *                           anticipated when the completion callback is triggered, and can
-         *                           be used play effects back-to-back.
-         */
-        SingleVibratorStep(long startTime, VibratorController controller,
-                VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
-            super(startTime);
-            this.controller = controller;
-            this.effect = effect;
-            this.segmentIndex = index;
-            this.vibratorOffTimeout = vibratorOffTimeout;
-        }
-
-        @Override
-        public long getVibratorOnDuration() {
-            return mVibratorOnResult;
-        }
-
-        @Override
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
-            boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
-            mVibratorCallbackReceived |= isSameVibrator;
-            // Only anticipate this step if a timeout was set to wait for the vibration to complete,
-            // otherwise we are waiting for the correct time to play the next step.
-            return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
-        }
-
-        @Override
-        public List<Step> cancel() {
-            return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(),
-                    /* cancelled= */ true, controller, vibratorOffTimeout));
-        }
-
-        @Override
-        public void cancelImmediately() {
-            if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
-                // Vibrator might be running from previous steps, so turn it off while canceling.
-                stopVibrating();
-            }
-        }
-
-        void stopVibrating() {
-            if (DEBUG) {
-                Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
-            }
-            controller.off();
-        }
-
-        void changeAmplitude(float amplitude) {
-            if (DEBUG) {
-                Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
-                        + " to " + amplitude);
-            }
-            controller.setAmplitude(amplitude);
-        }
-
-        /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
-        public List<Step> skipToNextSteps(int segmentsSkipped) {
-            return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
-        }
-
-        /**
-         * Return the {@link #nextVibrateStep} with same start and off timings calculated from
-         * {@link #getVibratorOnDuration()}, jumping all played segments.
-         *
-         * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
-         * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
-         */
-        public List<Step> nextSteps(int segmentsPlayed) {
-            if (mVibratorOnResult <= 0) {
-                // Vibration was not started, so just skip the played segments and keep timings.
-                return skipToNextSteps(segmentsPlayed);
-            }
-            long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
-            long nextVibratorOffTimeout = nextStartTime + CALLBACKS_EXTRA_TIMEOUT;
-            return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
-        }
-
-        /**
-         * Return the {@link #nextVibrateStep} with given start and off timings, which might be
-         * calculated independently, jumping all played segments.
-         *
-         * <p>This should be used when the vibrator on/off state is not responsible for the steps
-         * execution timings, e.g. while playing the vibrator amplitudes.
-         */
-        public List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
-                int segmentsPlayed) {
-            Step nextStep = nextVibrateStep(nextStartTime, controller, effect,
-                    segmentIndex + segmentsPlayed, vibratorOffTimeout);
-            return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
-        }
-    }
-
-    /**
-     * Represent a step turn the vibrator on with a single prebaked effect.
-     *
-     * <p>This step automatically falls back by replacing the prebaked segment with
-     * {@link VibrationSettings#getFallbackEffect(int)}, if available.
-     */
-    private final class PerformStep extends SingleVibratorStep {
-
-        PerformStep(long startTime, VibratorController controller,
-                VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
-            // This step should wait for the last vibration to finish (with the timeout) and for the
-            // intended step start time (to respect the effect delays).
-            super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
-                    vibratorOffTimeout);
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformStep");
-            try {
-                VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
-                if (!(segment instanceof PrebakedSegment)) {
-                    Slog.w(TAG, "Ignoring wrong segment for a PerformStep: " + segment);
-                    return skipToNextSteps(/* segmentsSkipped= */ 1);
-                }
-
-                PrebakedSegment prebaked = (PrebakedSegment) segment;
-                if (DEBUG) {
-                    Slog.d(TAG, "Perform " + VibrationEffect.effectIdToString(
-                            prebaked.getEffectId()) + " on vibrator "
-                            + controller.getVibratorInfo().getId());
-                }
-
-                VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId());
-                mVibratorOnResult = controller.on(prebaked, mVibration.id);
-
-                if (mVibratorOnResult == 0 && prebaked.shouldFallback()
-                        && (fallback instanceof VibrationEffect.Composed)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Playing fallback for effect "
-                                + VibrationEffect.effectIdToString(prebaked.getEffectId()));
-                    }
-                    SingleVibratorStep fallbackStep = nextVibrateStep(startTime, controller,
-                            replaceCurrentSegment((VibrationEffect.Composed) fallback),
-                            segmentIndex, vibratorOffTimeout);
-                    List<Step> fallbackResult = fallbackStep.play();
-                    // Update the result with the fallback result so this step is seamlessly
-                    // replaced by the fallback to any outer application of this.
-                    mVibratorOnResult = fallbackStep.getVibratorOnDuration();
-                    return fallbackResult;
-                }
-
-                return nextSteps(/* segmentsPlayed= */ 1);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-
-        /**
-         * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
-         *
-         * @return a copy of {@link #effect} with replaced segment.
-         */
-        private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
-            List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
-            int newRepeatIndex = effect.getRepeatIndex();
-            newSegments.remove(segmentIndex);
-            newSegments.addAll(segmentIndex, fallback.getSegments());
-            if (segmentIndex < effect.getRepeatIndex()) {
-                newRepeatIndex += fallback.getSegments().size() - 1;
-            }
-            return new VibrationEffect.Composed(newSegments, newRepeatIndex);
-        }
-    }
-
-    /**
-     * Represent a step turn the vibrator on using a composition of primitives.
-     *
-     * <p>This step will use the maximum supported number of consecutive segments of type
-     * {@link PrimitiveSegment} starting at the current index.
-     */
-    private final class ComposePrimitivesStep extends SingleVibratorStep {
-
-        ComposePrimitivesStep(long startTime, VibratorController controller,
-                VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
-            // This step should wait for the last vibration to finish (with the timeout) and for the
-            // intended step start time (to respect the effect delays).
-            super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
-                    vibratorOffTimeout);
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
-            try {
-                // Load the next PrimitiveSegments to create a single compose call to the vibrator,
-                // limited to the vibrator composition maximum size.
-                int limit = controller.getVibratorInfo().getCompositionSizeMax();
-                int segmentCount = limit > 0
-                        ? Math.min(effect.getSegments().size(), segmentIndex + limit)
-                        : effect.getSegments().size();
-                List<PrimitiveSegment> primitives = new ArrayList<>();
-                for (int i = segmentIndex; i < segmentCount; i++) {
-                    VibrationEffectSegment segment = effect.getSegments().get(i);
-                    if (segment instanceof PrimitiveSegment) {
-                        primitives.add((PrimitiveSegment) segment);
-                    } else {
-                        break;
-                    }
-                }
-
-                if (primitives.isEmpty()) {
-                    Slog.w(TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
-                            + effect.getSegments().get(segmentIndex));
-                    return skipToNextSteps(/* segmentsSkipped= */ 1);
-                }
-
-                if (DEBUG) {
-                    Slog.d(TAG, "Compose " + primitives + " primitives on vibrator "
-                            + controller.getVibratorInfo().getId());
-                }
-                mVibratorOnResult = controller.on(
-                        primitives.toArray(new PrimitiveSegment[primitives.size()]),
-                        mVibration.id);
-
-                return nextSteps(/* segmentsPlayed= */ primitives.size());
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
-
-    /**
-     * Represent a step turn the vibrator on using a composition of PWLE segments.
-     *
-     * <p>This step will use the maximum supported number of consecutive segments of type
-     * {@link StepSegment} or {@link RampSegment} starting at the current index.
-     */
-    private final class ComposePwleStep extends SingleVibratorStep {
-
-        ComposePwleStep(long startTime, VibratorController controller,
-                VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
-            // This step should wait for the last vibration to finish (with the timeout) and for the
-            // intended step start time (to respect the effect delays).
-            super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
-                    vibratorOffTimeout);
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
-            try {
-                // Load the next RampSegments to create a single composePwle call to the vibrator,
-                // limited to the vibrator PWLE maximum size.
-                int limit = controller.getVibratorInfo().getPwleSizeMax();
-                int segmentCount = limit > 0
-                        ? Math.min(effect.getSegments().size(), segmentIndex + limit)
-                        : effect.getSegments().size();
-                List<RampSegment> pwles = new ArrayList<>();
-                for (int i = segmentIndex; i < segmentCount; i++) {
-                    VibrationEffectSegment segment = effect.getSegments().get(i);
-                    if (segment instanceof RampSegment) {
-                        pwles.add((RampSegment) segment);
-                    } else {
-                        break;
-                    }
-                }
-
-                if (pwles.isEmpty()) {
-                    Slog.w(TAG, "Ignoring wrong segment for a ComposePwleStep: "
-                            + effect.getSegments().get(segmentIndex));
-                    return skipToNextSteps(/* segmentsSkipped= */ 1);
-                }
-
-                if (DEBUG) {
-                    Slog.d(TAG, "Compose " + pwles + " PWLEs on vibrator "
-                            + controller.getVibratorInfo().getId());
-                }
-                mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
-                        mVibration.id);
-
-                return nextSteps(/* segmentsPlayed= */ pwles.size());
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
-
-    /**
-     * Represents a step to complete a {@link VibrationEffect}.
-     *
-     * <p>This runs right at the time the vibration is considered to end and will update the pending
-     * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
-     */
-    private final class CompleteStep extends SingleVibratorStep {
-        private final boolean mCancelled;
-
-        CompleteStep(long startTime, boolean cancelled, VibratorController controller,
-                long vibratorOffTimeout) {
-            super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
-            mCancelled = cancelled;
-        }
-
-        @Override
-        public boolean isCleanUp() {
-            // If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
-            // Otherwise this step is part of the vibration.
-            return mCancelled;
-        }
-
-        @Override
-        public List<Step> cancel() {
-            if (mCancelled) {
-                // Double cancelling will just turn off the vibrator right away.
-                return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
-            }
-            return super.cancel();
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep");
-            try {
-                if (DEBUG) {
-                    Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
-                            + " step on vibrator " + controller.getVibratorInfo().getId());
-                }
-                if (mVibratorCallbackReceived) {
-                    // Vibration completion callback was received by this step, just turn if off
-                    // and skip any clean-up.
-                    stopVibrating();
-                    return EMPTY_STEP_LIST;
-                }
-
-                float currentAmplitude = controller.getCurrentAmplitude();
-                long remainingOnDuration =
-                        vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis();
-                long rampDownDuration =
-                        Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration());
-                long stepDownDuration = mVibrationSettings.getRampStepDuration();
-                if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN
-                        || rampDownDuration <= stepDownDuration) {
-                    // No need to ramp down the amplitude, just wait to turn it off.
-                    if (mCancelled) {
-                        // Vibration is completing because it was cancelled, turn off right away.
-                        stopVibrating();
-                        return EMPTY_STEP_LIST;
-                    } else {
-                        return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
-                    }
-                }
-
-                if (DEBUG) {
-                    Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId()
-                            + " from amplitude " + currentAmplitude
-                            + " for " + rampDownDuration + "ms");
-                }
-                float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
-                float amplitudeTarget = currentAmplitude - amplitudeDelta;
-                long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout;
-                return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta,
-                        controller, newVibratorOffTimeout));
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
-
-    /** Represents a step to ramp down the vibrator amplitude before turning it off. */
-    private final class RampOffStep extends SingleVibratorStep {
-        private final float mAmplitudeTarget;
-        private final float mAmplitudeDelta;
-
-        RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta,
-                VibratorController controller, long vibratorOffTimeout) {
-            super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
-            mAmplitudeTarget = amplitudeTarget;
-            mAmplitudeDelta = amplitudeDelta;
-        }
-
-        @Override
-        public boolean isCleanUp() {
-            return true;
-        }
-
-        @Override
-        public List<Step> cancel() {
-            return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep");
-            try {
-                if (DEBUG) {
-                    long latency = SystemClock.uptimeMillis() - startTime;
-                    Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
-                            + latency + "ms latency.");
-                }
-                if (mVibratorCallbackReceived) {
-                    // Vibration completion callback was received by this step, just turn if off
-                    // and skip the rest of the steps to ramp down the vibrator amplitude.
-                    stopVibrating();
-                    return EMPTY_STEP_LIST;
-                }
-
-                changeAmplitude(mAmplitudeTarget);
-
-                float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
-                if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) {
-                    // Vibrator amplitude cannot go further down, just turn it off.
-                    return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
-                }
-                return Arrays.asList(new RampOffStep(
-                        startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget,
-                        mAmplitudeDelta, controller, vibratorOffTimeout));
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
-
-    /**
-     * Represents a step to turn the vibrator off.
-     *
-     * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
-     * and can anticipated by vibrator complete callbacks.
-     */
-    private final class OffStep extends SingleVibratorStep {
-
-        OffStep(long startTime, VibratorController controller) {
-            super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
-        }
-
-        @Override
-        public boolean isCleanUp() {
-            return true;
-        }
-
-        @Override
-        public List<Step> cancel() {
-            return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
-        }
-
-        @Override
-        public void cancelImmediately() {
-            stopVibrating();
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
-            try {
-                stopVibrating();
-                return EMPTY_STEP_LIST;
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-    }
-
-    /**
-     * Represents a step to turn the vibrator on and change its amplitude.
-     *
-     * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
-     * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
-     */
-    private final class AmplitudeStep extends SingleVibratorStep {
-        private long mNextOffTime;
-
-        AmplitudeStep(long startTime, VibratorController controller,
-                VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
-            // This step has a fixed startTime coming from the timings of the waveform it's playing.
-            super(startTime, controller, effect, index, vibratorOffTimeout);
-            mNextOffTime = vibratorOffTimeout;
-        }
-
-        @Override
-        public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
-            if (controller.getVibratorInfo().getId() == vibratorId) {
-                mVibratorCallbackReceived = true;
-                mNextOffTime = SystemClock.uptimeMillis();
-            }
-            // Timings are tightly controlled here, so only anticipate if the vibrator was supposed
-            // to be ON but has completed prematurely, to turn it back on as soon as possible.
-            return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
-        }
-
-        @Override
-        public List<Step> play() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
-            try {
-                long now = SystemClock.uptimeMillis();
-                long latency = now - startTime;
-                if (DEBUG) {
-                    Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
-                }
-
-                if (mVibratorCallbackReceived && latency < 0) {
-                    // This step was anticipated because the vibrator turned off prematurely.
-                    // Turn it back on and return this same step to run at the exact right time.
-                    mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
-                    return Arrays.asList(new AmplitudeStep(startTime, controller, effect,
-                            segmentIndex, mNextOffTime));
-                }
-
-                VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
-                if (!(segment instanceof StepSegment)) {
-                    Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment);
-                    return skipToNextSteps(/* segmentsSkipped= */ 1);
-                }
-
-                StepSegment stepSegment = (StepSegment) segment;
-                if (stepSegment.getDuration() == 0) {
-                    // Skip waveform entries with zero timing.
-                    return skipToNextSteps(/* segmentsSkipped= */ 1);
-                }
-
-                float amplitude = stepSegment.getAmplitude();
-                if (amplitude == 0) {
-                    if (vibratorOffTimeout > now) {
-                        // Amplitude cannot be set to zero, so stop the vibrator.
-                        stopVibrating();
-                        mNextOffTime = now;
-                    }
-                } else {
-                    if (startTime >= mNextOffTime) {
-                        // Vibrator is OFF. Turn vibrator back on for the duration of another
-                        // cycle before setting the amplitude.
-                        long onDuration = getVibratorOnDuration(effect, segmentIndex);
-                        if (onDuration > 0) {
-                            mVibratorOnResult = startVibrating(onDuration);
-                            mNextOffTime = now + onDuration + CALLBACKS_EXTRA_TIMEOUT;
-                        }
-                    }
-                    changeAmplitude(amplitude);
-                }
-
-                // Use original startTime to avoid propagating latencies to the waveform.
-                long nextStartTime = startTime + segment.getDuration();
-                return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-
-        private long turnVibratorBackOn(long remainingDuration) {
-            long onDuration = getVibratorOnDuration(effect, segmentIndex);
-            if (onDuration <= 0) {
-                // Vibrator is supposed to go back off when this step starts, so just leave it off.
-                return vibratorOffTimeout;
-            }
-            onDuration += remainingDuration;
-            float expectedAmplitude = controller.getCurrentAmplitude();
-            mVibratorOnResult = startVibrating(onDuration);
-            if (mVibratorOnResult > 0) {
-                // Set the amplitude back to the value it was supposed to be playing at.
-                changeAmplitude(expectedAmplitude);
-            }
-            return SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
-        }
-
-        private long startVibrating(long duration) {
-            if (DEBUG) {
-                Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
-                        + duration + "ms");
-            }
-            return controller.on(duration, mVibration.id);
-        }
-
-        /**
-         * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
-         * until the next time it's vibrating amplitude is zero or a different type of segment is
-         * found.
-         */
-        private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
-            List<VibrationEffectSegment> segments = effect.getSegments();
-            int segmentCount = segments.size();
-            int repeatIndex = effect.getRepeatIndex();
-            int i = startIndex;
-            long timing = 0;
-            while (i < segmentCount) {
-                VibrationEffectSegment segment = segments.get(i);
-                if (!(segment instanceof StepSegment)
-                        || ((StepSegment) segment).getAmplitude() == 0) {
-                    break;
-                }
-                timing += segment.getDuration();
-                i++;
-                if (i == segmentCount && repeatIndex >= 0) {
-                    i = repeatIndex;
-                    // prevent infinite loop
-                    repeatIndex = -1;
-                }
-                if (i == startIndex) {
-                    // The repeating waveform keeps the vibrator ON all the time. Use a minimum
-                    // of 1s duration to prevent short patterns from turning the vibrator ON too
-                    // frequently.
-                    return Math.max(timing, 1000);
-                }
-            }
-            if (i == segmentCount && effect.getRepeatIndex() < 0) {
-                // Vibration ending at non-zero amplitude, add extra timings to ramp down after
-                // vibration is complete.
-                timing += mVibrationSettings.getRampDownDuration();
-            }
-            return timing;
-        }
-    }
-
-    /**
-     * Map a {@link CombinedVibration} to the vibrators available on the device.
-     *
-     * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
-     * play all of the effects in sync.
-     */
-    private final class DeviceEffectMap {
-        private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
-        private final int[] mVibratorIds;
-        private final long mRequiredSyncCapabilities;
-
-        DeviceEffectMap(CombinedVibration.Mono mono) {
-            mVibratorEffects = new SparseArray<>(mVibrators.size());
-            mVibratorIds = new int[mVibrators.size()];
-            for (int i = 0; i < mVibrators.size(); i++) {
-                int vibratorId = mVibrators.keyAt(i);
-                VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
-                VibrationEffect effect = mDeviceEffectAdapter.apply(mono.getEffect(), vibratorInfo);
-                if (effect instanceof VibrationEffect.Composed) {
-                    mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
-                    mVibratorIds[i] = vibratorId;
-                }
-            }
-            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
-        }
-
-        DeviceEffectMap(CombinedVibration.Stereo stereo) {
-            SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
-            mVibratorEffects = new SparseArray<>();
-            for (int i = 0; i < stereoEffects.size(); i++) {
-                int vibratorId = stereoEffects.keyAt(i);
-                if (mVibrators.contains(vibratorId)) {
-                    VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
-                    VibrationEffect effect = mDeviceEffectAdapter.apply(
-                            stereoEffects.valueAt(i), vibratorInfo);
-                    if (effect instanceof VibrationEffect.Composed) {
-                        mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
-                    }
-                }
-            }
-            mVibratorIds = new int[mVibratorEffects.size()];
-            for (int i = 0; i < mVibratorEffects.size(); i++) {
-                mVibratorIds[i] = mVibratorEffects.keyAt(i);
-            }
-            mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
-        }
-
-        /**
-         * Return the number of vibrators mapped to play the {@link CombinedVibration} on this
-         * device.
-         */
-        public int size() {
-            return mVibratorIds.length;
-        }
-
-        /**
-         * Return all capabilities required to play the {@link CombinedVibration} in
-         * between calls to {@link IVibratorManager#prepareSynced} and
-         * {@link IVibratorManager#triggerSynced}.
-         */
-        public long getRequiredSyncCapabilities() {
-            return mRequiredSyncCapabilities;
-        }
-
-        /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */
-        public int[] getVibratorIds() {
-            return mVibratorIds;
-        }
-
-        /** Return the id of the vibrator at given index. */
-        public int vibratorIdAt(int index) {
-            return mVibratorEffects.keyAt(index);
-        }
-
-        /** Return the {@link VibrationEffect} at given index. */
-        public VibrationEffect.Composed effectAt(int index) {
-            return mVibratorEffects.valueAt(index);
-        }
-
-        /**
-         * Return all capabilities required from the {@link IVibratorManager} to prepare and
-         * trigger all given effects in sync.
-         *
-         * @return {@link IVibratorManager#CAP_SYNC} together with all required
-         * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
-         */
-        private long calculateRequiredSyncCapabilities(
-                SparseArray<VibrationEffect.Composed> effects) {
-            long prepareCap = 0;
-            for (int i = 0; i < effects.size(); i++) {
-                VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
-                if (firstSegment instanceof StepSegment) {
-                    prepareCap |= IVibratorManager.CAP_PREPARE_ON;
-                } else if (firstSegment instanceof PrebakedSegment) {
-                    prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
-                } else if (firstSegment instanceof PrimitiveSegment) {
-                    prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
-                }
-            }
-            int triggerCap = 0;
-            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
-                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
-            }
-            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
-                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
-            }
-            if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
-                triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
-            }
-            return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
-        }
-
-        /**
-         * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
-         * different ones, requiring a mixed trigger capability from the vibrator manager for
-         * syncing all effects.
-         */
-        private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
-            return (prepareCapabilities & capability) != 0
-                    && (prepareCapabilities & ~capability) != 0;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index b2e34da..01f9d0b9 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -42,6 +42,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
@@ -60,6 +61,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -88,6 +90,9 @@
             VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
                     | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 
+    /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
+    private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
+
     /** Lifecycle responsible for initializing this class at the right system server phases. */
     public static class Lifecycle extends SystemService {
         private VibratorManagerService mService;
@@ -125,7 +130,8 @@
     private final long mCapabilities;
     private final int[] mVibratorIds;
     private final SparseArray<VibratorController> mVibrators;
-    private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
+    private final VibrationThreadCallbacks mVibrationThreadCallbacks =
+            new VibrationThreadCallbacks();
     @GuardedBy("mLock")
     private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
     @GuardedBy("mLock")
@@ -200,8 +206,7 @@
         mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
                 .getSystemUiServiceComponent().getPackageName();
 
-        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
-                BatteryStats.SERVICE_NAME));
+        mBatteryStatsService = injector.getBatteryStatsService();
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
@@ -633,8 +638,8 @@
             }
 
             VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings,
-                    mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService,
-                    mVibrationCallbacks);
+                    mDeviceVibrationEffectAdapter, mVibrators, mWakeLock,
+                    mVibrationThreadCallbacks);
 
             if (mCurrentVibration == null) {
                 return startVibrationThreadLocked(vibThread);
@@ -1104,6 +1109,11 @@
             return new Handler(looper);
         }
 
+        IBatteryStats getBatteryStatsService() {
+            return IBatteryStats.Stub.asInterface(ServiceManager.getService(
+                    BatteryStats.SERVICE_NAME));
+        }
+
         VibratorController createVibratorController(int vibratorId,
                 VibratorController.OnVibrationCompleteListener listener) {
             return new VibratorController(vibratorId, listener);
@@ -1115,10 +1125,10 @@
     }
 
     /**
-     * Implementation of {@link VibrationThread.VibrationCallbacks} that controls synced vibrations
-     * and reports them when finished.
+     * Implementation of {@link VibrationThread.VibratorManagerHooks} that controls synced
+     * vibrations and reports them when finished.
      */
-    private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
+    private final class VibrationThreadCallbacks implements VibrationThread.VibratorManagerHooks {
 
         @Override
         public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
@@ -1140,6 +1150,36 @@
         }
 
         @Override
+        public void noteVibratorOn(int uid, long duration) {
+            try {
+                if (duration <= 0) {
+                    return;
+                }
+                if (duration == Long.MAX_VALUE) {
+                    // Repeating duration has started. Report a fixed duration here, noteVibratorOff
+                    // should be called when this is cancelled.
+                    duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
+                }
+                mBatteryStatsService.noteVibratorOn(uid, duration);
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+                        uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
+                        duration);
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
+        public void noteVibratorOff(int uid) {
+            try {
+                mBatteryStatsService.noteVibratorOff(uid);
+                FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+                        uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
+                        /* duration= */ 0);
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
         public void onVibrationCompleted(long vibrationId, Vibration.Status status) {
             if (DEBUG) {
                 Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status);
@@ -1153,7 +1193,7 @@
         }
 
         @Override
-        public void onVibratorsReleased() {
+        public void onVibrationThreadReleased() {
             if (DEBUG) {
                 Slog.d(TAG, "Vibrators released after finished vibration");
             }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 87c8a79..2da2987 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -62,8 +62,8 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
 import android.graphics.Color;
+import android.graphics.ImageDecoder;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.display.DisplayManager;
@@ -199,6 +199,8 @@
     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
     static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
+    private static final String RECORD_FILE = "decode_record";
+    private static final String RECORD_LOCK_FILE = "decode_lock_record";
 
     // All the various per-user state files we need to be aware of
     private static final String[] sPerUserFiles = new String[] {
@@ -689,8 +691,7 @@
                 }
 
                 if (DEBUG) {
-                    // This is just a quick estimation, may be smaller than it is.
-                    long estimateSize = options.outWidth * options.outHeight * 4;
+                    long estimateSize = (long) options.outWidth * options.outHeight * 4;
                     Slog.v(TAG, "Null crop of new wallpaper, estimate size="
                             + estimateSize + ", success=" + success);
                 }
@@ -699,9 +700,6 @@
                 FileOutputStream f = null;
                 BufferedOutputStream bos = null;
                 try {
-                    BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
-                            wallpaper.wallpaperFile.getAbsolutePath(), false);
-
                     // This actually downsamples only by powers of two, but that's okay; we do
                     // a proper scaling blit later.  This is to minimize transient RAM use.
                     // We calculate the largest power-of-two under the actual ratio rather than
@@ -755,8 +753,24 @@
                         Slog.v(TAG, "  maxTextureSize=" + GLHelper.getMaxTextureSize());
                     }
 
-                    Bitmap cropped = decoder.decodeRegion(cropHint, options);
-                    decoder.recycle();
+                    //Create a record file and will delete if ImageDecoder work well.
+                    final String recordName =
+                            (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+                                    ? RECORD_FILE : RECORD_LOCK_FILE);
+                    final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
+                    record.createNewFile();
+                    Slog.v(TAG, "record path =" + record.getPath()
+                            + ", record name =" + record.getName());
+
+                    final ImageDecoder.Source srcData =
+                            ImageDecoder.createSource(wallpaper.wallpaperFile);
+                    final int sampleSize = scale;
+                    Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
+                        decoder.setTargetSampleSize(sampleSize);
+                        decoder.setCrop(estimateCrop);
+                    });
+
+                    record.delete();
 
                     if (cropped == null) {
                         Slog.e(TAG, "Could not decode new wallpaper");
@@ -1819,6 +1833,7 @@
                     new UserSwitchObserver() {
                         @Override
                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+                            errorCheck(newUserId);
                             switchUser(newUserId, reply);
                         }
                     }, TAG);
@@ -1856,6 +1871,14 @@
 
     @Override
     public void onBootPhase(int phase) {
+        // If someone set too large jpg file as wallpaper, system_server may be killed by lmk in
+        // generateCrop(), so we create a file in generateCrop() before ImageDecoder starts working
+        // and delete this file after ImageDecoder finishing. If the specific file exists, that
+        // means ImageDecoder can't handle the original wallpaper file, in order to avoid
+        // system_server restart again and again and rescue party will trigger factory reset,
+        // so we reset default wallpaper in case system_server is trapped into a restart loop.
+        errorCheck(UserHandle.USER_SYSTEM);
+
         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
             systemReady();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -1863,6 +1886,38 @@
         }
     }
 
+    private static final HashMap<Integer, String> sWallpaperType = new HashMap<Integer, String>() {
+        {
+            put(FLAG_SYSTEM, RECORD_FILE);
+            put(FLAG_LOCK, RECORD_LOCK_FILE);
+        }
+    };
+
+    private void errorCheck(int userID) {
+        sWallpaperType.forEach((type, filename) -> {
+            final File record = new File(getWallpaperDir(userID), filename);
+            if (record.exists()) {
+                Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type
+                        + ", wallpaper fail detect!! reset to default wallpaper");
+                clearWallpaperData(userID, type);
+                record.delete();
+            }
+        });
+    }
+
+    private void clearWallpaperData(int userID, int wallpaperType) {
+        final WallpaperData wallpaper = new WallpaperData(userID, getWallpaperDir(userID),
+                (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER,
+                (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP);
+        if (wallpaper.sourceExists()) {
+            wallpaper.wallpaperFile.delete();
+        }
+        if (wallpaper.cropExists()) {
+            wallpaper.cropFile.delete();
+        }
+
+    }
+
     @Override
     public void onUnlockUser(final int userId) {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 76e1c43..a4a200d 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -756,6 +756,19 @@
         }
     }
 
+    @Override
+    public void setPreferDockBigOverlays(IBinder token, boolean preferDockBigOverlays) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                r.setPreferDockBigOverlays(preferDockBigOverlays);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     /**
      * Splash screen view is attached to activity.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a8dd856..a32d45c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -68,6 +68,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
 
@@ -102,6 +103,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -742,6 +744,12 @@
             info.mReason = activityToReason.valueAt(index);
             info.mLoggedTransitionStarting = true;
             if (info.mIsDrawn) {
+                if (info.mReason == APP_TRANSITION_RECENTS_ANIM) {
+                    final LatencyTracker latencyTracker = r.mWmService.mLatencyTracker;
+                    final int duration = info.mSourceEventDelayMs + info.mCurrentTransitionDelayMs;
+                    mLoggerHandler.post(() -> latencyTracker.logAction(
+                            LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration));
+                }
                 done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dc53cc6..b0efa5b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -348,6 +348,8 @@
 import com.android.server.wm.WindowManagerService.H;
 import com.android.server.wm.utils.InsetUtils;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -530,6 +532,7 @@
         // activity can enter picture in picture while pausing (only when switching to another task)
     PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
         // The PiP params used when deferring the entering of picture-in-picture.
+    boolean preferDockBigOverlays;
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
     ComponentName requestedVrComponent; // the requested component for handling VR mode.
@@ -625,6 +628,10 @@
     // it references to gets removed. This should also be cleared when we move out of pip.
     private Task mLastParentBeforePip;
 
+    // Only set if this instance is a launch-into-pip Activity, points to the
+    // host Activity the launch-into-pip Activity is originated from.
+    private ActivityRecord mLaunchIntoPipHostActivity;
+
     boolean firstWindowDrawn;
     /** Whether the visible window(s) of this activity is drawn. */
     private boolean mReportedDrawn;
@@ -942,6 +949,7 @@
         }
     };
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final long now = SystemClock.uptimeMillis();
@@ -1221,6 +1229,9 @@
         if (mLastParentBeforePip != null) {
             pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
         }
+        if (mLaunchIntoPipHostActivity != null) {
+            pw.println(prefix + "launchIntoPipHostActivity=" + mLaunchIntoPipHostActivity);
+        }
 
         mLetterboxUiController.dump(pw, prefix);
 
@@ -1555,10 +1566,16 @@
     /**
      * Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
      * {@link #getTask()} is set before this is called.
+     *
+     * @param launchIntoPipHostActivity {@link ActivityRecord} as the host Activity for the
+     *        launch-int-pip Activity see also {@link #mLaunchIntoPipHostActivity}.
      */
-    void setLastParentBeforePip() {
-        mLastParentBeforePip = getTask();
+    void setLastParentBeforePip(@Nullable ActivityRecord launchIntoPipHostActivity) {
+        mLastParentBeforePip = (launchIntoPipHostActivity == null)
+                ? getTask()
+                : launchIntoPipHostActivity.getTask();
         mLastParentBeforePip.mChildPipActivity = this;
+        mLaunchIntoPipHostActivity = launchIntoPipHostActivity;
     }
 
     private void clearLastParentBeforePip() {
@@ -1566,12 +1583,17 @@
             mLastParentBeforePip.mChildPipActivity = null;
             mLastParentBeforePip = null;
         }
+        mLaunchIntoPipHostActivity = null;
     }
 
     @Nullable Task getLastParentBeforePip() {
         return mLastParentBeforePip;
     }
 
+    @Nullable ActivityRecord getLaunchIntoPipHostActivity() {
+        return mLaunchIntoPipHostActivity;
+    }
+
     private void updateColorTransform() {
         if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
             getPendingTransaction().setColorTransform(mSurfaceControl,
@@ -1852,6 +1874,10 @@
                 mRotationAnimationHint = rotationAnimation;
             }
 
+            if (options.getLaunchIntoPipParams() != null) {
+                pictureInPictureArgs = options.getLaunchIntoPipParams();
+            }
+
             mOverrideTaskTransition = options.getOverrideTaskTransition();
         }
 
@@ -1956,6 +1982,8 @@
         mLetterboxUiController = new LetterboxUiController(mWmService, this);
         mCameraCompatControlEnabled = mWmService.mContext.getResources()
                 .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
+        preferDockBigOverlays = mWmService.mContext.getResources()
+                .getBoolean(R.bool.config_dockBigOverlayWindows);
 
         if (_createTime > 0) {
             createTime = _createTime;
@@ -2509,7 +2537,6 @@
         }
         removeStartingWindowAnimation(true /* prepareAnimation */);
 
-        // TODO(b/215316431): Add tests
         final Task task = getTask();
         if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
                 && task != null) {
@@ -7689,7 +7716,6 @@
      *     once the starting window is removed in {@link #removeStartingWindow}).
      * </ul>
      */
-    // TODO(b/215316431): Add tests
     boolean isEligibleForLetterboxEducation() {
         return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
                 && mIsEligibleForFixedOrientationLetterbox
@@ -9430,6 +9456,11 @@
         getTask().getRootTask().onPictureInPictureParamsChanged();
     }
 
+    void setPreferDockBigOverlays(boolean preferDockBigOverlays) {
+        this.preferDockBigOverlays = preferDockBigOverlays;
+        getTask().getRootTask().onPreferDockBigOverlaysChanged();
+    }
+
     @Override
     boolean isSyncFinished() {
         if (!super.isSyncFinished()) return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5ffe214..ef0ee12 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1896,6 +1896,14 @@
         mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
                 mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);
 
+        // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
+        // Note that mStartActivity and source should be in the same Task at this point.
+        if (mOptions != null && mOptions.isLaunchIntoPip()
+                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
+            mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
+                    sourceRecord, "launch-into-pip");
+        }
+
         return START_SUCCESS;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8d99f07..0497477 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1468,7 +1468,7 @@
 
             a.packageName = process.mInfo.packageName;
             a.applicationInfo = process.mInfo;
-            a.processName = process.mInfo.processName;
+            a.processName = process.mName;
             a.uiOptions = process.mInfo.uiOptions;
             a.taskAffinity = "android:" + a.packageName + "/dream";
 
@@ -3538,8 +3538,8 @@
                 final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                 final float expandedAspectRatio = r.pictureInPictureArgs.getExpandedAspectRatio();
                 final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                mRootWindowContainer.moveActivityToPinnedRootTask(
-                        r, "enterPictureInPictureMode");
+                mRootWindowContainer.moveActivityToPinnedRootTask(r,
+                        null /* launchIntoPipHostActivity */, "enterPictureInPictureMode");
                 final Task task = r.getTask();
                 task.setPictureInPictureAspectRatio(aspectRatio, expandedAspectRatio);
                 task.setPictureInPictureActions(actions);
@@ -3922,11 +3922,11 @@
     @Override
     public void onPictureInPictureStateChanged(PictureInPictureUiState pipState) {
         enforceTaskPermission("onPictureInPictureStateChanged");
-        final Task rootPinnedStask = mRootWindowContainer.getDefaultTaskDisplayArea()
+        final Task rootPinnedTask = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .getRootPinnedTask();
-        if (rootPinnedStask != null && rootPinnedStask.getTopMostActivity() != null) {
+        if (rootPinnedTask != null && rootPinnedTask.getTopMostActivity() != null) {
             mWindowManager.mAtmService.mActivityClientController.onPictureInPictureStateChanged(
-                    rootPinnedStask.getTopMostActivity(), pipState);
+                    rootPinnedTask.getTopMostActivity(), pipState);
         }
     }
 
@@ -6099,7 +6099,8 @@
         public boolean onForceStopPackage(String packageName, boolean doit, boolean evenPersistent,
                 int userId) {
             synchronized (mGlobalLock) {
-
+                // In case if setWindowManager hasn't been called yet when booting.
+                if (mRootWindowContainer == null) return false;
                 return mRootWindowContainer.finishDisabledPackageActivities(packageName,
                         null /* filterByClasses */, doit, evenPersistent, userId,
                         // Only remove the activities without process because the activities with
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index e26748c..9893f68 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -53,7 +53,11 @@
      * Returns true if the back predictability feature is enabled
      */
     static boolean isEnabled() {
-        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
+    }
+
+    static boolean isScreenshotEnabled() {
+        return false;
     }
 
     /**
@@ -101,6 +105,7 @@
 
         synchronized (task.mWmService.mGlobalLock) {
             activityRecord = task.topRunningActivity();
+
             removedWindowContainer = activityRecord;
             taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
             WindowState window = task.getWindow(WindowState::isFocused);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 132396b..0868111 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -652,7 +652,9 @@
         void prepareSurfaces() {
             mDimmer.resetDimStates();
             super.prepareSurfaces();
+            // Bounds need to be relative, as the dim layer is a child.
             getBounds(mTmpDimBoundsRect);
+            mTmpDimBoundsRect.offsetTo(0 /* newLeft */, 0 /* newTop */);
 
             // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
             // on the display level fades out.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e845034..fd24565 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -457,8 +457,6 @@
      */
     private boolean mLastWallpaperVisible = false;
 
-    private Rect mBaseDisplayRect = new Rect();
-
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
     int pendingLayoutChanges;
@@ -488,9 +486,6 @@
     private final Rect mTmpRect2 = new Rect();
     private final Region mTmpRegion = new Region();
 
-    /** Used for handing back size of display */
-    private final Rect mTmpBounds = new Rect();
-
     private final Configuration mTmpConfiguration = new Configuration();
 
     /** Remove this display when animation on it has completed. */
@@ -1362,8 +1357,8 @@
         return mDisplayRotation;
     }
 
-    void setInsetProvider(@InternalInsetsType int type, WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider){
+    void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider) {
         setInsetProvider(type, win, frameProvider, null /* imeFrameProvider */);
     }
 
@@ -1377,10 +1372,10 @@
      * @param imeFrameProvider Function to compute the frame when dispatching insets to the IME, or
      *                         {@code null} if the normal frame should be taken.
      */
-    void setInsetProvider(@InternalInsetsType int type, WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
-        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider,
+    void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindowContainer(win, frameProvider,
                 imeFrameProvider);
     }
 
@@ -2080,8 +2075,6 @@
         mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
                 mDisplayInfo);
 
-        mBaseDisplayRect.set(0, 0, dw, dh);
-
         if (isDefaultDisplay) {
             mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
                     mCompatDisplayMetrics);
@@ -2213,14 +2206,14 @@
      */
     void computeScreenConfiguration(Configuration config) {
         final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
-        calculateBounds(displayInfo, mTmpBounds);
-        config.windowConfiguration.setBounds(mTmpBounds);
-        config.windowConfiguration.setMaxBounds(mTmpBounds);
+        final int dw = displayInfo.logicalWidth;
+        final int dh = displayInfo.logicalHeight;
+        mTmpRect.set(0, 0, dw, dh);
+        config.windowConfiguration.setBounds(mTmpRect);
+        config.windowConfiguration.setMaxBounds(mTmpRect);
         config.windowConfiguration.setWindowingMode(getWindowingMode());
         config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
 
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
         computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
                 displayInfo.displayCutout);
 
@@ -2844,10 +2837,6 @@
 
     /** Update base (override) display metrics. */
     void updateBaseDisplayMetrics(int baseWidth, int baseHeight, int baseDensity) {
-        final int originalWidth = mBaseDisplayWidth;
-        final int originalHeight = mBaseDisplayHeight;
-        final int originalDensity = mBaseDisplayDensity;
-
         mBaseDisplayWidth = baseWidth;
         mBaseDisplayHeight = baseHeight;
         mBaseDisplayDensity = baseDensity;
@@ -2867,12 +2856,6 @@
                         + mBaseDisplayHeight + " on display:" + getDisplayId());
             }
         }
-
-        if (mBaseDisplayWidth != originalWidth || mBaseDisplayHeight != originalHeight
-                || mBaseDisplayDensity != originalDensity) {
-            mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
-            updateBounds();
-        }
     }
 
     /**
@@ -3021,7 +3004,7 @@
         if (focusedTask == null) {
             mTouchExcludeRegion.setEmpty();
         } else {
-            mTouchExcludeRegion.set(mBaseDisplayRect);
+            mTouchExcludeRegion.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
             mTmpRect.setEmpty();
             mTmpRect2.setEmpty();
@@ -3343,8 +3326,8 @@
         }
         if (mInsetsStateController != null) {
             for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
-                final InsetsSourceProvider provider = mInsetsStateController.peekSourceProvider(
-                        type);
+                final WindowContainerInsetsSourceProvider provider = mInsetsStateController
+                        .peekSourceProvider(type);
                 if (provider != null) {
                     provider.dumpDebug(proto, type == ITYPE_IME ? IME_INSETS_SOURCE_PROVIDER :
                             INSETS_SOURCE_PROVIDERS, logLevel);
@@ -3822,7 +3805,7 @@
             final int imePid = mInputMethodWindow.mSession.mPid;
             mAtmService.onImeWindowSetOnDisplayArea(imePid, mImeWindowsContainer);
         }
-        mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
+        mInsetsStateController.getSourceProvider(ITYPE_IME).setWindowContainer(win,
                 mDisplayPolicy.getImeSourceFrameProvider(), null /* imeFrameProvider */);
         computeImeTarget(true /* updateImeTarget */);
         updateImeControlTarget();
@@ -4053,7 +4036,7 @@
         // app.
         assignWindowLayers(true /* setLayoutNeeded */);
         // 3. The z-order of IME might have been changed. Update the above insets state.
-        mInsetsStateController.updateAboveInsetsState(mInputMethodWindow,
+        mInsetsStateController.updateAboveInsetsState(
                 mInsetsStateController.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
         // 4. Update the IME control target to apply any inset change and animation.
         // 5. Reparent the IME container surface to either the input target app, or the IME window
@@ -4184,7 +4167,7 @@
         if (mImeInputTarget != target) {
             ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
             setImeInputTarget(target);
-            mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
+            mInsetsStateController.updateAboveInsetsState(mInsetsStateController
                     .getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
             updateImeControlTarget();
         }
@@ -4587,25 +4570,6 @@
         }
     }
 
-    private void updateBounds() {
-        calculateBounds(mDisplayInfo, mTmpBounds);
-        setBounds(mTmpBounds);
-    }
-
-    // Determines the current display bounds based on the current state
-    private void calculateBounds(DisplayInfo displayInfo, Rect out) {
-        // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
-        final int rotation = displayInfo.rotation;
-        boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
-        final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
-        int width = displayInfo.logicalWidth;
-        int left = (physWidth - width) / 2;
-        int height = displayInfo.logicalHeight;
-        int top = (physHeight - height) / 2;
-        out.set(left, top, left + width, top + height);
-    }
-
     private void getBounds(Rect out, @Rotation int rotation) {
         getBounds(out);
 
@@ -4846,6 +4810,17 @@
         }
 
         @Override
+        void updateAboveInsetsState(InsetsState aboveInsetsState,
+                SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+                ArraySet<WindowState> insetsChangedWindows) {
+            if (skipImeWindowsDuringTraversal(mDisplayContent)) {
+                return;
+            }
+            super.updateAboveInsetsState(aboveInsetsState, localInsetsSourceProvidersFromParent,
+                    insetsChangedWindows);
+        }
+
+        @Override
         boolean forAllWindows(ToBooleanFunction<WindowState> callback,
                 boolean traverseTopToBottom) {
             final DisplayContent dc = mDisplayContent;
@@ -5524,7 +5499,7 @@
      * display, which set unrestricted keep-clear areas.
      *
      * For context on restricted vs unrestricted keep-clear areas, see
-     * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}.
+     * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS}.
      */
     void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) {
         final Matrix tmpMatrix = new Matrix();
@@ -5561,8 +5536,6 @@
     }
 
     void onDisplayChanged() {
-        mDisplay.getRealSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
         final int lastDisplayState = mDisplayInfo.state;
         updateDisplayInfo();
 
@@ -6441,6 +6414,8 @@
 
         @Override
         public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+            // It is only needed when freezing display in legacy transition.
+            if (mTransitionController.isShellTransitionsEnabled()) return;
             continueUpdateOrientationForDiffOrienLaunchingApp();
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 30fffd3..4148d8b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1106,8 +1106,8 @@
                 break;
             case TYPE_STATUS_BAR:
                 mStatusBar = win;
-                final TriConsumer<DisplayFrames, WindowState, Rect> gestureFrameProvider =
-                        (displayFrames, windowState, rect) -> {
+                final TriConsumer<DisplayFrames, WindowContainer, Rect> gestureFrameProvider =
+                        (displayFrames, windowContainer, rect) -> {
                             rect.bottom = rect.top + getStatusBarHeight(displayFrames);
                             final DisplayCutout cutout =
                                     displayFrames.mInsetsState.getDisplayCutout();
@@ -1128,24 +1128,25 @@
             case TYPE_NAVIGATION_BAR:
                 mNavigationBar = win;
                 mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             if (!mNavButtonForcedVisible) {
-                                inOutFrame.inset(windowState.getLayoutingAttrs(
+                                inOutFrame.inset(win.getLayoutingAttrs(
                                         displayFrames.mRotation).providedInternalInsets);
                                 inOutFrame.inset(win.mGivenContentInsets);
                             }
                         },
 
                         // For IME we use regular frame.
-                        (displayFrames, windowState, inOutFrame) ->
-                                inOutFrame.set(windowState.getFrame()));
+                        (displayFrames, windowContainer, inOutFrame) -> {
+                            inOutFrame.set(win.getFrame());
+                        });
 
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             inOutFrame.top -= mBottomGestureAdditionalInset;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_LEFT_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             final int leftSafeInset =
                                     Math.max(displayFrames.mDisplayCutoutSafe.left, 0);
                             inOutFrame.left = 0;
@@ -1154,7 +1155,7 @@
                             inOutFrame.right = leftSafeInset + mLeftGestureInset;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
-                        (displayFrames, windowState, inOutFrame) -> {
+                        (displayFrames, windowContainer, inOutFrame) -> {
                             final int rightSafeInset =
                                     Math.min(displayFrames.mDisplayCutoutSafe.right,
                                             displayFrames.mUnrestricted.right);
@@ -1164,8 +1165,8 @@
                             inOutFrame.right = displayFrames.mDisplayWidth;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_TAPPABLE_ELEMENT, win,
-                        (displayFrames, windowState, inOutFrame) -> {
-                            if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
+                        (displayFrames, windowContainer, inOutFrame) -> {
+                            if ((win.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
                                     || mNavigationBarLetsThroughTaps) {
                                 inOutFrame.setEmpty();
                             }
@@ -1176,11 +1177,13 @@
             default:
                 if (attrs.providesInsetsTypes != null) {
                     for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
-                        final TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider =
+                        final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider =
                                 !attrs.providedInternalImeInsets.equals(Insets.NONE)
-                                        ? (displayFrames, windowState, inOutFrame) ->
-                                        inOutFrame.inset(windowState.getLayoutingAttrs(
-                                                displayFrames.mRotation).providedInternalImeInsets)
+                                        ? (displayFrames, windowContainer, inOutFrame) -> {
+                                            inOutFrame.inset(win.getLayoutingAttrs(
+                                                    displayFrames.mRotation)
+                                                    .providedInternalImeInsets);
+                                        }
                                         : null;
                         switch (insetsType) {
                             case ITYPE_STATUS_BAR:
@@ -1201,10 +1204,9 @@
                                 break;
                         }
                         mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
-                                windowState, inOutFrame) -> {
-                            inOutFrame.inset(
-                                    windowState.getLayoutingAttrs(displayFrames.mRotation)
-                                            .providedInternalInsets);
+                                windowContainer, inOutFrame) -> {
+                            inOutFrame.inset(win.getLayoutingAttrs(
+                                    displayFrames.mRotation).providedInternalInsets);
                             inOutFrame.inset(win.mGivenContentInsets);
                         }, imeFrameProvider);
                         mInsetsSourceWindowsExceptIme.add(win);
@@ -1230,8 +1232,13 @@
         }
     }
 
-    TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() {
-        return (displayFrames, windowState, inOutFrame) -> {
+    TriConsumer<DisplayFrames, WindowContainer, Rect> getImeSourceFrameProvider() {
+        return (displayFrames, windowContainer, inOutFrame) -> {
+            WindowState windowState = windowContainer.asWindowState();
+            if (windowState == null) {
+                throw new IllegalArgumentException("IME insets must be provided by a window.");
+            }
+
             if (mNavigationBar != null && navigationBarPosition(displayFrames.mRotation)
                     == NAV_BAR_BOTTOM) {
                 // In gesture navigation, nav bar frame is larger than frame to calculate insets.
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index cbefe7f..f24e429 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -46,7 +46,7 @@
  * Controller for IME inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
  */
-final class ImeInsetsSourceProvider extends InsetsSourceProvider {
+final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider {
 
     private InsetsControlTarget mImeRequester;
     private Runnable mShowImeRunner;
@@ -113,8 +113,8 @@
     private void reportImeDrawnForOrganizer(InsetsControlTarget caller) {
         if (caller.getWindow() != null && caller.getWindow().getTask() != null) {
             if (caller.getWindow().getTask().isOrganized()) {
-                mWin.mWmService.mAtmService.mTaskOrganizerController.reportImeDrawnOnTask(
-                        caller.getWindow().getTask());
+                mWindowContainer.mWmService.mAtmService.mTaskOrganizerController
+                        .reportImeDrawnOnTask(caller.getWindow().getTask());
             }
         }
     }
@@ -173,12 +173,18 @@
     }
 
     void checkShowImePostLayout() {
+        if (mWindowContainer == null) {
+            return;
+        }
+        WindowState windowState =  mWindowContainer.asWindowState();
+        if (windowState == null) {
+            throw new IllegalArgumentException("IME insets must be provided by a window.");
+        }
         // check if IME is drawn
         if (mIsImeLayoutDrawn
                 || (isReadyToShowIme()
-                && mWin != null
-                && mWin.isDrawn()
-                && !mWin.mGivenInsetsPending)) {
+                && windowState.isDrawn()
+                && !windowState.mGivenInsetsPending)) {
             mIsImeLayoutDrawn = true;
             // show IME if InputMethodService requested it to be shown.
             if (mShowImeRunner != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d28dfd5..bb6d83c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -19,24 +19,36 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.StatusBarManager;
+import android.app.WindowConfiguration;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.util.ArrayMap;
 import android.util.IntArray;
 import android.util.SparseArray;
 import android.view.InsetsAnimationControlCallbacks;
@@ -164,8 +176,10 @@
     }
 
     boolean isHidden(@InternalInsetsType int type) {
-        final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
-        return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
+        final WindowContainerInsetsSourceProvider provider = mStateController
+                .peekSourceProvider(type);
+        return provider != null && provider.hasWindowContainer()
+                && !provider.getSource().isVisible();
     }
 
     void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
@@ -235,10 +249,11 @@
     }
 
     /**
-     * @see InsetsStateController#getInsetsForWindow
+     * Adjusts the sources in {@code originalState} to account for things like transient bars, IME
+     * & rounded corners.
      */
-    InsetsState getInsetsForWindow(WindowState target, boolean includesTransient) {
-        final InsetsState originalState = mStateController.getInsetsForWindow(target);
+    InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState,
+            boolean includesTransient) {
         InsetsState state;
         if (!includesTransient) {
             state = adjustVisibilityForTransientTypes(originalState);
@@ -249,17 +264,131 @@
         return adjustInsetsForRoundedCorners(target, state, state == originalState);
     }
 
-    InsetsState getInsetsForWindow(WindowState target) {
-        return getInsetsForWindow(target, false);
+    InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) {
+        return adjustInsetsForWindow(target, originalState, false);
+    }
+
+    /**
+     * @see WindowState#getInsetsState()
+     */
+    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
+        final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
+        final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
+        if (token != null) {
+            final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                return rotatedState;
+            }
+        }
+        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
+        // Always use windowing mode fullscreen when get insets for window metrics to make sure it
+        // contains all insets types.
+        final InsetsState originalState = mDisplayContent.getInsetsPolicy()
+                .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
+                        mStateController.getRawInsetsState());
+        return adjustVisibilityForTransientTypes(originalState);
+    }
+
+    private static @InternalInsetsType int getInsetsTypeForLayoutParams(
+            WindowManager.LayoutParams attrs) {
+        @WindowManager.LayoutParams.WindowType int type = attrs.type;
+        switch (type) {
+            case TYPE_STATUS_BAR:
+                return ITYPE_STATUS_BAR;
+            case TYPE_NAVIGATION_BAR:
+                return ITYPE_NAVIGATION_BAR;
+            case TYPE_INPUT_METHOD:
+                return ITYPE_IME;
+        }
+
+        // If not one of the types above, check whether an internal inset mapping is specified.
+        if (attrs.providesInsetsTypes != null) {
+            for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+                switch (insetsType) {
+                    case ITYPE_STATUS_BAR:
+                    case ITYPE_NAVIGATION_BAR:
+                    case ITYPE_CLIMATE_BAR:
+                    case ITYPE_EXTRA_NAVIGATION_BAR:
+                        return insetsType;
+                }
+            }
+        }
+
+        return ITYPE_INVALID;
     }
 
 
     /**
-     * @see InsetsStateController#getInsetsForWindowMetrics
+     * Modifies the given {@code state} according to the {@code type} (Inset type) provided by
+     * the target.
+     * When performing layout of the target or dispatching insets to the target, we need to exclude
+     * sources which should not be visible to the target. e.g., the source which represents the
+     * target window itself, and the IME source when the target is above IME. We also need to
+     * exclude certain types of insets source for client within specific windowing modes.
+     *
+     * @param type the inset type provided by the target
+     * @param windowingMode the windowing mode of the target
+     * @param isAlwaysOnTop is the target always on top
+     * @param state the input inset state containing all the sources
+     * @return The state stripped of the necessary information.
      */
-    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
-        final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs);
-        return adjustVisibilityForTransientTypes(originalState);
+    InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
+            @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
+            InsetsState state) {
+        boolean stateCopied = false;
+
+        if (type != ITYPE_INVALID) {
+            state = new InsetsState(state);
+            stateCopied = true;
+            state.removeSource(type);
+
+            // Navigation bar doesn't get influenced by anything else
+            if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
+                state.removeSource(ITYPE_IME);
+                state.removeSource(ITYPE_STATUS_BAR);
+                state.removeSource(ITYPE_CLIMATE_BAR);
+                state.removeSource(ITYPE_CAPTION_BAR);
+                state.removeSource(ITYPE_NAVIGATION_BAR);
+                state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+            }
+
+            // Status bar doesn't get influenced by caption bar
+            if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
+                state.removeSource(ITYPE_CAPTION_BAR);
+            }
+
+            // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
+            if (type == ITYPE_IME) {
+                ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
+                        .getSourceProviders();
+                for (int i = providers.size() - 1; i >= 0; i--) {
+                    WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
+                    if (otherProvider.overridesImeFrame()) {
+                        InsetsSource override =
+                                new InsetsSource(
+                                        state.getSource(otherProvider.getSource().getType()));
+                        override.setFrame(otherProvider.getImeOverrideFrame());
+                        state.addSource(override);
+                    }
+                }
+            }
+        }
+
+        if (WindowConfiguration.isFloating(windowingMode)
+                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
+            if (!stateCopied) {
+                state = new InsetsState(state);
+                stateCopied = true;
+            }
+            state.removeSource(ITYPE_STATUS_BAR);
+            state.removeSource(ITYPE_NAVIGATION_BAR);
+            state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
+            if (windowingMode == WINDOWING_MODE_PINNED) {
+                state.removeSource(ITYPE_IME);
+            }
+        }
+
+        return state;
     }
 
     private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
@@ -534,7 +663,7 @@
         final IntArray showingTransientTypes = mShowingTransientTypes;
         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
             final @InternalInsetsType int type = showingTransientTypes.get(i);
-            InsetsSourceProvider provider = mStateController.getSourceProvider(type);
+            WindowContainerInsetsSourceProvider provider = mStateController.getSourceProvider(type);
             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
             if (control == null || control.getLeash() == null) {
                 continue;
@@ -644,7 +773,7 @@
                 }
                 mAnimatingShown = show;
 
-                final InsetsState state = getInsetsForWindow(mFocusedWin);
+                final InsetsState state = mFocusedWin.getInsetsState();
 
                 // We are about to playing the default animation. Passing a null frame indicates
                 // the controlled types should be animated regardless of the frame.
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 21eea94..047bf2f 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -65,11 +65,11 @@
  * Controller for a specific inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
  */
-class InsetsSourceProvider {
+abstract class InsetsSourceProvider {
 
     protected final DisplayContent mDisplayContent;
     protected final @NonNull InsetsSource mSource;
-    protected WindowState mWin;
+    protected WindowContainer mWindowContainer;
 
     private final Rect mTmpRect = new Rect();
     private final InsetsStateController mStateController;
@@ -80,8 +80,8 @@
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
     private @Nullable ControlAdapter mAdapter;
-    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
-    private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider;
+    private TriConsumer<DisplayFrames, WindowContainer, Rect> mFrameProvider;
+    private TriConsumer<DisplayFrames, WindowContainer, Rect> mImeFrameProvider;
     private final Rect mImeOverrideFrame = new Rect();
     private boolean mIsLeashReadyForDispatching;
     private final Rect mLastSourceFrame = new Rect();
@@ -100,7 +100,8 @@
     private boolean mClientVisible;
 
     /**
-     * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
+     * Whether the window container is available and considered visible as in
+     * {@link WindowContainer#isVisible}.
      */
     private boolean mServerVisible;
 
@@ -109,8 +110,8 @@
     private final boolean mControllable;
 
     /**
-     * Whether to forced the dimensions of the source window to the inset frame and crop out any
-     * overflow.
+     * Whether to forced the dimensions of the source window container to the inset frame and crop
+     * out any overflow.
      * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar
      * rounded corners overlays.
      *
@@ -152,42 +153,42 @@
     }
 
     /**
-     * Updates the window that currently backs this source.
+     * Updates the window container that currently backs this source.
      *
-     * @param win The window that links to this source.
+     * @param windowContainer The window container that links to this source.
      * @param frameProvider Based on display frame state and the window, calculates the resulting
      *                      frame that should be reported to clients.
      * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
      *                         frame that should be reported to IME.
      */
-    void setWindow(@Nullable WindowState win,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
-            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
-        if (mWin != null) {
+    void setWindowContainer(@Nullable WindowContainer windowContainer,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
+            @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+        if (mWindowContainer != null) {
             if (mControllable) {
-                mWin.setControllableInsetProvider(null);
+                mWindowContainer.setControllableInsetProvider(null);
             }
-            // The window may be animating such that we can hand out the leash to the control
-            // target. Revoke the leash by cancelling the animation to correct the state.
+            // The window container may be animating such that we can hand out the leash to the
+            // control target. Revoke the leash by cancelling the animation to correct the state.
             // TODO: Ideally, we should wait for the animation to finish so previous window can
             // animate-out as new one animates-in.
-            mWin.cancelAnimation();
-            mWin.mProvidedInsetsSources.remove(mSource.getType());
+            mWindowContainer.cancelAnimation();
+            mWindowContainer.getProvidedInsetsSources().remove(mSource.getType());
             mSeamlessRotating = false;
         }
-        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", win,
-                InsetsState.typeToString(mSource.getType()));
-        mWin = win;
+        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s",
+                windowContainer, InsetsState.typeToString(mSource.getType()));
+        mWindowContainer = windowContainer;
         mFrameProvider = frameProvider;
         mImeFrameProvider = imeFrameProvider;
-        if (win == null) {
+        if (windowContainer == null) {
             setServerVisible(false);
             mSource.setFrame(new Rect());
             mSource.setVisibleFrame(null);
         } else {
-            mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
+            mWindowContainer.getProvidedInsetsSources().put(mSource.getType(), mSource);
             if (mControllable) {
-                mWin.setControllableInsetProvider(this);
+                mWindowContainer.setControllableInsetProvider(this);
                 if (mPendingControlTarget != null) {
                     updateControlForTarget(mPendingControlTarget, true /* force */);
                     mPendingControlTarget = null;
@@ -197,18 +198,39 @@
     }
 
     /**
-     * @return Whether there is a window which backs this source.
+     * @return Whether there is a window container which backs this source.
      */
-    boolean hasWindow() {
-        return mWin != null;
+    boolean hasWindowContainer() {
+        return mWindowContainer != null;
     }
 
     /**
      * The source frame can affect the layout of other windows, so this should be called once the
-     * window gets laid out.
+     * window container gets laid out.
      */
     void updateSourceFrame() {
-        if (mWin == null || mWin.mGivenInsetsPending) {
+        if (mWindowContainer == null) {
+            return;
+        }
+        WindowState win = mWindowContainer.asWindowState();
+
+        if (win == null) {
+            // For all the non window WindowContainers.
+            if (mServerVisible) {
+                mTmpRect.set(mWindowContainer.getBounds());
+                if (mFrameProvider != null) {
+                    mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                            mWindowContainer, mTmpRect);
+                }
+            } else {
+                mTmpRect.setEmpty();
+            }
+            mSource.setFrame(mTmpRect);
+            mSource.setVisibleFrame(null);
+            return;
+        }
+
+        if (win.mGivenInsetsPending) {
             // If the given insets are pending, they are not reliable for now. The source frame
             // should be updated after the new given insets are sent to window manager.
             return;
@@ -218,11 +240,12 @@
         // frame may not yet determined that server side doesn't think the window is ready to
         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
         if (mServerVisible) {
-            mTmpRect.set(mWin.getFrame());
+            mTmpRect.set(win.getFrame());
             if (mFrameProvider != null) {
-                mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+                mFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                        mWindowContainer, mTmpRect);
             } else {
-                mTmpRect.inset(mWin.mGivenContentInsets);
+                mTmpRect.inset(win.mGivenContentInsets);
             }
         } else {
             mTmpRect.setEmpty();
@@ -230,15 +253,17 @@
         mSource.setFrame(mTmpRect);
 
         if (mImeFrameProvider != null) {
-            mImeOverrideFrame.set(mWin.getFrame());
-            mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin,
+            mImeOverrideFrame.set(win.getFrame());
+            mImeFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
+                    mWindowContainer,
                     mImeOverrideFrame);
         }
 
-        if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
-                || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
-            mTmpRect.set(mWin.getFrame());
-            mTmpRect.inset(mWin.mGivenVisibleInsets);
+        if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0
+                || win.mGivenVisibleInsets.right != 0
+                || win.mGivenVisibleInsets.bottom != 0) {
+            mTmpRect.set(win.getFrame());
+            mTmpRect.inset(win.mGivenVisibleInsets);
             mSource.setVisibleFrame(mTmpRect);
         } else {
             mSource.setVisibleFrame(null);
@@ -253,7 +278,7 @@
         source.setVisible(mSource.isVisible());
         mTmpRect.set(winFrame);
         if (mFrameProvider != null) {
-            mFrameProvider.accept(displayFrames, mWin, mTmpRect);
+            mFrameProvider.accept(displayFrames, mWindowContainer, mTmpRect);
         }
         source.setFrame(mTmpRect);
         return source;
@@ -263,27 +288,30 @@
      * Called when a layout pass has occurred.
      */
     void onPostLayout() {
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             return;
         }
-
-        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy());
+        WindowState windowState = mWindowContainer.asWindowState();
+        boolean isServerVisible = windowState != null
+                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+                : mWindowContainer.isVisibleRequested();
+        setServerVisible(isServerVisible);
         updateSourceFrame();
         if (mControl != null) {
             boolean changed = false;
             final Point position = getWindowFrameSurfacePosition();
             if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
                 changed = true;
-                if (mWin.getWindowFrames().didFrameSizeChange() && mWin.mWinAnimator.getShown()
-                        && mWin.okToDisplay()) {
-                    mWin.applyWithNextDraw(mSetLeashPositionConsumer);
+                if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
+                        && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
+                    windowState.applyWithNextDraw(mSetLeashPositionConsumer);
                 } else {
-                    mSetLeashPositionConsumer.accept(mWin.getSyncTransaction());
+                    mSetLeashPositionConsumer.accept(mWindowContainer.getSyncTransaction());
                 }
             }
             if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
                 final Insets insetsHint = mSource.calculateInsets(
-                        mWin.getBounds(), true /* ignoreVisibility */);
+                        mWindowContainer.getBounds(), true /* ignoreVisibility */);
                 if (!insetsHint.equals(mControl.getInsetsHint())) {
                     changed = true;
                     mControl.setInsetsHint(insetsHint);
@@ -297,17 +325,19 @@
     }
 
     private Point getWindowFrameSurfacePosition() {
+        WindowState win = mWindowContainer.asWindowState();
         if (mControl != null) {
             final AsyncRotationController controller =
-                    mWin.mDisplayContent.getAsyncRotationController();
-            if (controller != null && controller.shouldFreezeInsetsPosition(mWin)) {
+                    win.mDisplayContent.getAsyncRotationController();
+            if (controller != null && controller.shouldFreezeInsetsPosition(win)) {
                 // Use previous position because the fade-out animation runs in old rotation.
                 return mControl.getSurfacePosition();
             }
         }
-        final Rect frame = mWin.getFrame();
+        final Rect frame = mWindowContainer.asWindowState() != null
+                ? mWindowContainer.asWindowState().getFrame() : mWindowContainer.getBounds();
         final Point position = new Point();
-        mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
+        mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position);
         return position;
     }
 
@@ -322,8 +352,8 @@
     }
 
     /**
-     * Ensures that the inset source window is cropped so that anything that doesn't fit within the
-     * inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
+     * Ensures that the inset source window container is cropped so that anything that doesn't fit
+     * within the inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
      *
      * The inset source surface will get cropped to the be of the size of the insets it's providing.
      *
@@ -342,9 +372,10 @@
     void setCropToProvidingInsetsBounds(Transaction t) {
         mCropToProvidingInsets = true;
 
-        if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+        if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
             // apply to existing leash
-            t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, getProvidingInsetsBoundsCropRect());
+            t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash,
+                    getProvidingInsetsBoundsCropRect());
         }
     }
 
@@ -359,13 +390,15 @@
         mCropToProvidingInsets = false;
 
         // apply to existing leash
-        if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
-            t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, null);
+        if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
+            t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash, null);
         }
     }
 
     private Rect getProvidingInsetsBoundsCropRect() {
-        Rect sourceWindowFrame = mWin.getFrame();
+        Rect sourceWindowFrame = mWindowContainer.asWindowState() != null
+                ? mWindowContainer.asWindowState().getFrame()
+                : mWindowContainer.getBounds();
         Rect insetFrame = getSource().getFrame();
 
         // The rectangle in buffer space we want to crop to
@@ -384,11 +417,11 @@
             return;
         }
 
-        if (mWin != null && mWin.getSurfaceControl() == null) {
+        if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) {
             // if window doesn't have a surface, set it null and return.
-            setWindow(null, null, null);
+            setWindowContainer(null, null, null);
         }
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             mPendingControlTarget = target;
             return;
         }
@@ -397,7 +430,7 @@
         }
         if (target == null) {
             // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
-            mWin.cancelAnimation();
+            mWindowContainer.cancelAnimation();
             setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
             return;
         }
@@ -407,7 +440,7 @@
             setClientVisible(target.getRequestedVisibility(mSource.getType()));
         }
         final Transaction t = mDisplayContent.getSyncTransaction();
-        mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
+        mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
                 ANIMATION_TYPE_INSETS_CONTROL);
 
         // The leash was just created. We cannot dispatch it until its surface transaction is
@@ -418,16 +451,16 @@
         mControlTarget = target;
         updateVisibility();
         mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition,
-                mSource.calculateInsets(mWin.getBounds(), true /* ignoreVisibility */));
+                mSource.calculateInsets(mWindowContainer.getBounds(), true /* ignoreVisibility */));
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
     }
 
     void startSeamlessRotation() {
-      if (!mSeamlessRotating) {
-          mSeamlessRotating = true;
-          mWin.cancelAnimation();
-      }
+        if (!mSeamlessRotating) {
+            mSeamlessRotating = true;
+            mWindowContainer.cancelAnimation();
+        }
     }
 
     void finishSeamlessRotation() {
@@ -475,10 +508,13 @@
     }
 
     private boolean isMirroredSource() {
-        if (mWin == null) {
+        if (mWindowContainer == null) {
             return false;
         }
-        final int[] provides = mWin.mAttrs.providesInsetsTypes;
+        if (mWindowContainer.asWindowState() == null) {
+            return false;
+        }
+        final int[] provides = ((WindowState) mWindowContainer).mAttrs.providesInsetsTypes;
         if (provides == null) {
             return false;
         }
@@ -542,9 +578,9 @@
         pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
         pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toShortString());
         pw.println();
-        if (mWin != null) {
-            pw.print(prefix + "mWin=");
-            pw.println(mWin);
+        if (mWindowContainer != null) {
+            pw.print(prefix + "mWindowContainer=");
+            pw.println(mWindowContainer);
         }
         if (mAdapter != null) {
             pw.print(prefix + "mAdapter=");
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a1468cc..78608e2 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,41 +16,28 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.WindowingMode;
 import android.graphics.Rect;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
-import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams.WindowType;
 
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -68,7 +55,8 @@
     private final InsetsState mState = new InsetsState();
     private final DisplayContent mDisplayContent;
 
-    private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
+    private final ArrayMap<Integer, WindowContainerInsetsSourceProvider> mProviders =
+            new ArrayMap<>();
     private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
             new ArrayMap<>();
     private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
@@ -103,134 +91,6 @@
         mDisplayContent = displayContent;
     }
 
-    /**
-     * Gets the insets state from the perspective of the target. When performing layout of the
-     * target or dispatching insets to the target, we need to exclude sources which should not be
-     * visible to the target. e.g., the source which represents the target window itself, and the
-     * IME source when the target is above IME. We also need to exclude certain types of insets
-     * source for client within specific windowing modes.
-     * This is to get the insets for a window layout on the screen. If the window is not there, use
-     * the {@link #getInsetsForWindowMetrics} to get insets instead.
-     *
-     * @param target The window associate with the perspective.
-     * @return The state stripped of the necessary information.
-     */
-    InsetsState getInsetsForWindow(@NonNull WindowState target) {
-        final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
-        if (rotatedState != null) {
-            return rotatedState;
-        }
-        final InsetsSourceProvider provider = target.getControllableInsetProvider();
-        final @InternalInsetsType int type = provider != null
-                ? provider.getSource().getType() : ITYPE_INVALID;
-        return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
-                target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
-                        (target.mAttrs.receiveInsetsIgnoringZOrder ? mState :
-                         target.mAboveInsetsState));
-    }
-
-    InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
-        final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
-        final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
-        if (token != null) {
-            final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
-            if (rotatedState != null) {
-                return rotatedState;
-            }
-        }
-        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
-        // Always use windowing mode fullscreen when get insets for window metrics to make sure it
-        // contains all insets types.
-        return getInsetsForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop, mState);
-    }
-
-    private static @InternalInsetsType
-            int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) {
-        @WindowType int type = attrs.type;
-        switch (type) {
-            case TYPE_STATUS_BAR:
-                return ITYPE_STATUS_BAR;
-            case TYPE_NAVIGATION_BAR:
-                return ITYPE_NAVIGATION_BAR;
-            case TYPE_INPUT_METHOD:
-                return ITYPE_IME;
-        }
-
-        // If not one of the types above, check whether an internal inset mapping is specified.
-        if (attrs.providesInsetsTypes != null) {
-            for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
-                switch (insetsType) {
-                    case ITYPE_STATUS_BAR:
-                    case ITYPE_NAVIGATION_BAR:
-                    case ITYPE_CLIMATE_BAR:
-                    case ITYPE_EXTRA_NAVIGATION_BAR:
-                        return insetsType;
-                }
-            }
-        }
-
-        return ITYPE_INVALID;
-    }
-
-    /**
-     * @see #getInsetsForWindow
-     * @see #getInsetsForWindowMetrics
-     */
-    private InsetsState getInsetsForTarget(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
-        boolean stateCopied = false;
-
-        if (type != ITYPE_INVALID) {
-            state = new InsetsState(state);
-            stateCopied = true;
-            state.removeSource(type);
-
-            // Navigation bar doesn't get influenced by anything else
-            if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
-                state.removeSource(ITYPE_STATUS_BAR);
-                state.removeSource(ITYPE_CLIMATE_BAR);
-                state.removeSource(ITYPE_CAPTION_BAR);
-                state.removeSource(ITYPE_NAVIGATION_BAR);
-                state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
-            }
-
-            // Status bar doesn't get influenced by caption bar
-            if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
-                state.removeSource(ITYPE_CAPTION_BAR);
-            }
-
-            // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
-            if (type == ITYPE_IME) {
-                for (int i = mProviders.size() - 1; i >= 0; i--) {
-                    InsetsSourceProvider otherProvider = mProviders.valueAt(i);
-                    if (otherProvider.overridesImeFrame()) {
-                        InsetsSource override =
-                                new InsetsSource(
-                                        state.getSource(otherProvider.getSource().getType()));
-                        override.setFrame(otherProvider.getImeOverrideFrame());
-                        state.addSource(override);
-                    }
-                }
-            }
-        }
-
-        if (WindowConfiguration.isFloating(windowingMode)
-                || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
-            if (!stateCopied) {
-                state = new InsetsState(state);
-                stateCopied = true;
-            }
-            state.removeSource(ITYPE_STATUS_BAR);
-            state.removeSource(ITYPE_NAVIGATION_BAR);
-            state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
-            if (windowingMode == WINDOWING_MODE_PINNED) {
-                state.removeSource(ITYPE_IME);
-            }
-        }
-
-        return state;
-    }
-
     InsetsState getRawInsetsState() {
         return mState;
     }
@@ -248,17 +108,22 @@
         return result;
     }
 
+    ArrayMap<Integer, WindowContainerInsetsSourceProvider> getSourceProviders() {
+        return mProviders;
+    }
+
     /**
      * @return The provider of a specific type.
      */
-    InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
+    WindowContainerInsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
         if (type == ITYPE_IME) {
             return mProviders.computeIfAbsent(type,
                     key -> new ImeInsetsSourceProvider(
                             mState.getSource(key), this, mDisplayContent));
         } else {
             return mProviders.computeIfAbsent(type,
-                    key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+                    key -> new WindowContainerInsetsSourceProvider(mState.getSource(key), this,
+                            mDisplayContent));
         }
     }
 
@@ -269,7 +134,8 @@
     /**
      * @return The provider of a specific type or null if we don't have it.
      */
-    @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
+    @Nullable
+    WindowContainerInsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
         return mProviders.get(type);
     }
 
@@ -289,58 +155,25 @@
     }
 
     /**
-     * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
-     * z-order of a window is changed.
+     * Updates {@link WindowState#mAboveInsetsState} for all windows in the display.
      *
-     * @param win The window whose z-order has changed.
      * @param notifyInsetsChange {@code true} if the clients should be notified about the change.
      */
-    void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
-        if (win == null || win.getDisplayContent() != mDisplayContent) {
-            return;
-        }
-        final boolean[] aboveWin = { true };
+    void updateAboveInsetsState(boolean notifyInsetsChange) {
         final InsetsState aboveInsetsState = new InsetsState();
         aboveInsetsState.set(mState,
                 displayCutout() | systemGestures() | mandatorySystemGestures());
-        final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources;
-        final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
-        mDisplayContent.forAllWindows(w -> {
-            if (aboveWin[0]) {
-                if (w == win) {
-                    aboveWin[0] = false;
-                    if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
-                        win.mAboveInsetsState.set(aboveInsetsState);
-                        insetsChangedWindows.add(win);
-                    }
-                    return winProvidedSources.size() == 0;
-                } else {
-                    final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
-                    for (int i = providedSources.size() - 1; i >= 0; i--) {
-                        aboveInsetsState.addSource(providedSources.valueAt(i));
-                    }
-                    if (winProvidedSources.size() == 0) {
-                        return false;
-                    }
-                    boolean changed = false;
-                    for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
-                        changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
-                    }
-                    if (changed) {
-                        insetsChangedWindows.add(w);
-                    }
-                }
-            } else {
-                for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
-                    w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
-                }
-                insetsChangedWindows.add(w);
-            }
-            return false;
-        }, true /* traverseTopToBottom */);
+        final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>();
+        final SparseArray<InsetsSourceProvider>
+                localInsetsSourceProvidersFromParent = new SparseArray<>();
+        // This method will iterate on the entire hierarchy in top to bottom z-order manner. The
+        // aboveInsetsState will be modified as per the insets provided by the WindowState being
+        // visited.
+        mDisplayContent.updateAboveInsetsState(aboveInsetsState,
+                localInsetsSourceProvidersFromParent, insetsChangedWindows);
         if (notifyInsetsChange) {
             for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
-                mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+                mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i));
             }
         }
     }
@@ -381,8 +214,8 @@
     void computeSimulatedState(WindowState win, DisplayFrames displayFrames, Rect winFrame) {
         final InsetsState state = displayFrames.mInsetsState;
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            final InsetsSourceProvider provider = mProviders.valueAt(i);
-            if (provider.mWin == win) {
+            final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
+            if (provider.mWindowContainer == win) {
                 state.addSource(provider.createSimulatedSource(displayFrames, winFrame));
             }
         }
@@ -438,7 +271,7 @@
         if (target == previous) {
             return;
         }
-        final InsetsSourceProvider provider = mProviders.get(type);
+        final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
         if (provider == null) {
             return;
         }
@@ -469,7 +302,7 @@
         if (fakeTarget == previous) {
             return;
         }
-        final InsetsSourceProvider provider = mProviders.get(type);
+        final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
         if (provider == null) {
             return;
         }
@@ -524,7 +357,7 @@
         }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             for (int i = mProviders.size() - 1; i >= 0; i--) {
-                final InsetsSourceProvider provider = mProviders.valueAt(i);
+                final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
                 provider.onSurfaceTransactionApplied();
             }
             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
new file mode 100644
index 0000000..6e8beee
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
@@ -0,0 +1,53 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.InsetsSource;
+
+/**
+ * An {@link InsetsSourceProvider} which doesn't have a backing window or a window container.
+ */
+public class RectInsetsSourceProvider extends InsetsSourceProvider {
+    private static final String TAG = TAG_WITH_CLASS_NAME
+            ? RectInsetsSourceProvider.class.getSimpleName()
+            : TAG_WM;
+
+    RectInsetsSourceProvider(InsetsSource source,
+            InsetsStateController stateController, DisplayContent displayContent) {
+        super(source, stateController, displayContent);
+    }
+
+    /**
+     * Sets the given {@code rect} as the frame of the underlying {@link InsetsSource}.
+     */
+    void setRect(Rect rect) {
+        mSource.setFrame(rect);
+        mSource.setVisible(true);
+    }
+
+    @Override
+    void onPostLayout() {
+        if (WindowManagerDebugConfig.DEBUG) {
+            Slog.d(TAG, "onPostLayout(), not calling super.onPostLayout().");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d535018..8ab2ee0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1979,7 +1979,8 @@
                 onTop);
     }
 
-    void moveActivityToPinnedRootTask(ActivityRecord r, String reason) {
+    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
+            @Nullable ActivityRecord launchIntoPipHostActivity, String reason) {
         mService.deferWindowLayout();
 
         final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
@@ -2030,7 +2031,7 @@
                         .setHasBeenVisible(true)
                         .build();
                 // Establish bi-directional link between the original and pinned task.
-                r.setLastParentBeforePip();
+                r.setLastParentBeforePip(launchIntoPipHostActivity);
                 // It's possible the task entering PIP is in freeform, so save the last
                 // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
                 // to its previous freeform bounds.
@@ -2091,6 +2092,10 @@
             r.setWindowingMode(intermediateWindowingMode);
             r.mWaitForEnteringPinnedMode = true;
             rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+            // Set the launch bounds for launch-into-pip Activity on the root task.
+            if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
+                rootTask.setBounds(r.getOptions().getLaunchBounds());
+            }
             rootTask.setDeferTaskAppear(false);
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index d1460f4..6bb63d9 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -108,15 +108,12 @@
      */
     private SurfaceControl mBackColorSurface;
     private BlackFrame mEnteringBlackFrame;
-    private int mWidth, mHeight;
 
     private final int mOriginalRotation;
     private final int mOriginalWidth;
     private final int mOriginalHeight;
     private int mCurRotation;
 
-    private Rect mOriginalDisplayRect = new Rect();
-    private Rect mCurrentDisplayRect = new Rect();
     // The current active animation to move from the old to the new rotated
     // state.  Which animation is run here will depend on the old and new
     // rotations.
@@ -127,7 +124,6 @@
     private boolean mAnimRunning;
     private boolean mFinishAnimReady;
     private long mFinishAnimStartTime;
-    private boolean mForceDefaultOrientation;
     private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
     /** Intensity of light/whiteness of the layout before rotation occurs. */
     private float mStartLuma;
@@ -138,25 +134,13 @@
         mService = displayContent.mWmService;
         mContext = mService.mContext;
         mDisplayContent = displayContent;
-        displayContent.getBounds(mOriginalDisplayRect);
+        final Rect currentBounds = displayContent.getBounds();
+        final int width = currentBounds.width();
+        final int height = currentBounds.height();
 
         // Screenshot does NOT include rotation!
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final int realOriginalRotation = displayInfo.rotation;
-        final int originalWidth;
-        final int originalHeight;
-        if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
-            // Emulated orientation.
-            mForceDefaultOrientation = true;
-            originalWidth = displayContent.mBaseDisplayWidth;
-            originalHeight = displayContent.mBaseDisplayHeight;
-        } else {
-            // Normal situation
-            originalWidth = displayInfo.logicalWidth;
-            originalHeight = displayInfo.logicalHeight;
-        }
-        mWidth = originalWidth;
-        mHeight = originalHeight;
 
         mOriginalRotation = originalRotation;
         // If the delta is not zero, the rotation of display may not change, but we still want to
@@ -165,8 +149,8 @@
         // when restoring the rotated app to the same rotation as current display.
         final int delta = deltaRotation(originalRotation, realOriginalRotation);
         final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
-        mOriginalWidth = flipped ? originalHeight : originalWidth;
-        mOriginalHeight = flipped ? originalWidth : originalHeight;
+        mOriginalWidth = flipped ? height : width;
+        mOriginalHeight = flipped ? width : height;
         mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
 
         // Check whether the current screen contains any secure content.
@@ -179,7 +163,7 @@
                     new SurfaceControl.LayerCaptureArgs.Builder(displayContent.getSurfaceControl())
                             .setCaptureSecureLayers(true)
                             .setAllowProtected(true)
-                            .setSourceCrop(new Rect(0, 0, mWidth, mHeight))
+                            .setSourceCrop(new Rect(0, 0, width, height))
                             .build();
             SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                     SurfaceControl.captureLayers(args);
@@ -244,6 +228,19 @@
             Slog.w(TAG, "Unable to allocate freeze surface", e);
         }
 
+        // If display size is changed with the same orientation, map the bounds of screenshot to
+        // the new logical display size. Currently pending transaction and RWC#mDisplayTransaction
+        // are merged to global transaction, so it can be synced with display change when calling
+        // DisplayManagerInternal#performTraversal(transaction).
+        final int logicalWidth = displayInfo.logicalWidth;
+        final int logicalHeight = displayInfo.logicalHeight;
+        if (logicalWidth > mOriginalWidth == logicalHeight > mOriginalHeight
+                && (logicalWidth != mOriginalWidth || logicalHeight != mOriginalHeight)) {
+            displayContent.getPendingTransaction().setGeometry(mScreenshotLayer,
+                    new Rect(0, 0, mOriginalWidth, mOriginalHeight),
+                    new Rect(0, 0, logicalWidth, logicalHeight), Surface.ROTATION_0);
+        }
+
         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                 "  FREEZE %s: CREATE", mScreenshotLayer);
         if (originalRotation == realOriginalRotation) {
@@ -277,11 +274,6 @@
         matrix.getValues(mTmpFloats);
         float x = mTmpFloats[Matrix.MTRANS_X];
         float y = mTmpFloats[Matrix.MTRANS_Y];
-        if (mForceDefaultOrientation) {
-            mDisplayContent.getBounds(mCurrentDisplayRect);
-            x -= mCurrentDisplayRect.left;
-            y -= mCurrentDisplayRect.top;
-        }
         t.setPosition(mScreenshotLayer, x, y);
         t.setMatrix(mScreenshotLayer,
                 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
@@ -293,8 +285,6 @@
 
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
-        pw.print(" mWidth="); pw.print(mWidth);
-        pw.print(" mHeight="); pw.println(mHeight);
         pw.print(prefix);
         pw.print("mEnteringBlackFrame=");
         pw.println(mEnteringBlackFrame);
@@ -315,11 +305,6 @@
         pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
         mSnapshotInitialMatrix.dump(pw); pw.println();
-        pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
-        if (mForceDefaultOrientation) {
-            pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
-            pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
-        }
     }
 
     public void setRotation(SurfaceControl.Transaction t, int rotation) {
@@ -329,7 +314,8 @@
         // to the snapshot to make it stay in the same original position
         // with the current screen rotation.
         int delta = deltaRotation(rotation, mOriginalRotation);
-        RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+        RotationAnimationUtils.createRotationMatrix(delta, mOriginalWidth, mOriginalHeight,
+                mSnapshotInitialMatrix);
         setRotationTransform(t, mSnapshotInitialMatrix);
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f7c5b26..ffa1a60 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2824,7 +2824,7 @@
         TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
         if (spec != null) {
             for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) {
-                InsetsSourceProvider insetProvider = getDisplayContent()
+                WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
                         .getInsetsStateController()
                         .getSourceProvider(insetType);
 
@@ -3397,6 +3397,13 @@
         info.positionInParent = getRelativePosition();
 
         info.pictureInPictureParams = getPictureInPictureParams(top);
+        info.preferDockBigOverlays = getPreferDockBigOverlays();
+        if (info.pictureInPictureParams != null
+                && info.pictureInPictureParams.isLaunchIntoPip()
+                && top.getTopMostActivity().getLastParentBeforePip() != null) {
+            info.launchIntoPipHostTaskId =
+                    top.getTopMostActivity().getLastParentBeforePip().mTaskId;
+        }
         info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null;
         info.topActivityInfo = mReuseActivitiesReport.top != null
                 ? mReuseActivitiesReport.top.info
@@ -3443,6 +3450,11 @@
                 ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
     }
 
+    private boolean getPreferDockBigOverlays() {
+        final ActivityRecord topMostActivity = getTopMostActivity();
+        return topMostActivity != null && topMostActivity.preferDockBigOverlays;
+    }
+
     Rect getDisplayCutoutInsets() {
         if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
         final WindowState w = getTopVisibleAppMainWindow();
@@ -4336,6 +4348,10 @@
         }
     }
 
+    void onPreferDockBigOverlaysChanged() {
+        dispatchTaskInfoChangedIfNeeded(true /* force */);
+    }
+
     /** Called when the top activity in the Root Task enters or exits size compat mode. */
     void onSizeCompatActivityChanged() {
         // Trigger TaskInfoChanged to update the size compat restart button.
@@ -6566,8 +6582,7 @@
             return;
         }
         if (mOverlayHost != null) {
-            final InsetsState s = getDisplayContent().getInsetsPolicy()
-                .getInsetsForWindow(originalChange, true);
+            final InsetsState s = originalChange.getInsetsState(true);
             getBounds(mTmpRect);
             mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
         }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1bba103..f0cca18 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -111,9 +110,6 @@
     private Task mRootHomeTask;
     private Task mRootPinnedTask;
 
-    // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
-    private Task mRootRecentsTask;
-
     private final ArrayList<WindowContainer> mTmpAlwaysOnTopChildren = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
@@ -222,8 +218,6 @@
     Task getRootTask(int windowingMode, int activityType) {
         if (activityType == ACTIVITY_TYPE_HOME) {
             return mRootHomeTask;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            return mRootRecentsTask;
         }
         if (windowingMode == WINDOWING_MODE_PINNED) {
             return mRootPinnedTask;
@@ -249,11 +243,6 @@
         return mRootHomeTask;
     }
 
-    @Nullable
-    Task getRootRecentsTask() {
-        return mRootRecentsTask;
-    }
-
     Task getRootPinnedTask() {
         return mRootPinnedTask;
     }
@@ -288,16 +277,6 @@
             } else {
                 mRootHomeTask = rootTask;
             }
-        } else if (rootTask.isActivityTypeRecents()) {
-            if (mRootRecentsTask != null) {
-                if (!rootTask.isDescendantOf(mRootRecentsTask)) {
-                    throw new IllegalArgumentException("addRootTaskReferenceIfNeeded: root recents"
-                            + " task=" + mRootRecentsTask + " already exist on display=" + this
-                            + " rootTask=" + rootTask);
-                }
-            } else {
-                mRootRecentsTask = rootTask;
-            }
         }
 
         if (!rootTask.isRootTask()) {
@@ -317,8 +296,6 @@
     void removeRootTaskReferenceIfNeeded(Task rootTask) {
         if (rootTask == mRootHomeTask) {
             mRootHomeTask = null;
-        } else if (rootTask == mRootRecentsTask) {
-            mRootRecentsTask = null;
         } else if (rootTask == mRootPinnedTask) {
             mRootPinnedTask = null;
         }
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
index d9dc9aa..c099628 100644
--- a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -19,51 +19,51 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.window.IOnFpsCallbackListener;
+import android.window.ITaskFpsCallback;
 
 import java.util.HashMap;
 
 final class TaskFpsCallbackController {
 
     private final Context mContext;
-    private final HashMap<IOnFpsCallbackListener, Long> mTaskFpsCallbackListeners;
-    private final HashMap<IOnFpsCallbackListener, IBinder.DeathRecipient> mDeathRecipients;
+    private final HashMap<ITaskFpsCallback, Long> mTaskFpsCallbacks;
+    private final HashMap<ITaskFpsCallback, IBinder.DeathRecipient> mDeathRecipients;
 
     TaskFpsCallbackController(Context context) {
         mContext = context;
-        mTaskFpsCallbackListeners = new HashMap<>();
+        mTaskFpsCallbacks = new HashMap<>();
         mDeathRecipients = new HashMap<>();
     }
 
-    void registerCallback(int taskId, IOnFpsCallbackListener listener) {
-        if (mTaskFpsCallbackListeners.containsKey(listener)) {
+    void registerListener(int taskId, ITaskFpsCallback callback) {
+        if (mTaskFpsCallbacks.containsKey(callback)) {
             return;
         }
 
-        final long nativeListener = nativeRegister(listener, taskId);
-        mTaskFpsCallbackListeners.put(listener, nativeListener);
+        final long nativeListener = nativeRegister(callback, taskId);
+        mTaskFpsCallbacks.put(callback, nativeListener);
 
-        final IBinder.DeathRecipient deathRecipient = () -> unregisterCallback(listener);
+        final IBinder.DeathRecipient deathRecipient = () -> unregisterListener(callback);
         try {
-            listener.asBinder().linkToDeath(deathRecipient, 0);
-            mDeathRecipients.put(listener, deathRecipient);
+            callback.asBinder().linkToDeath(deathRecipient, 0);
+            mDeathRecipients.put(callback, deathRecipient);
         } catch (RemoteException e) {
             // ignore
         }
     }
 
-    void unregisterCallback(IOnFpsCallbackListener listener) {
-        if (!mTaskFpsCallbackListeners.containsKey(listener)) {
+    void unregisterListener(ITaskFpsCallback callback) {
+        if (!mTaskFpsCallbacks.containsKey(callback)) {
             return;
         }
 
-        listener.asBinder().unlinkToDeath(mDeathRecipients.get(listener), 0);
-        mDeathRecipients.remove(listener);
+        callback.asBinder().unlinkToDeath(mDeathRecipients.get(callback), 0);
+        mDeathRecipients.remove(callback);
 
-        nativeUnregister(mTaskFpsCallbackListeners.get(listener));
-        mTaskFpsCallbackListeners.remove(listener);
+        nativeUnregister(mTaskFpsCallbacks.get(callback));
+        mTaskFpsCallbacks.remove(callback);
     }
 
-    private static native long nativeRegister(IOnFpsCallbackListener listener, int taskId);
+    private static native long nativeRegister(ITaskFpsCallback callback, int taskId);
     private static native void nativeUnregister(long ptr);
 }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c880aba..7fab94c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -31,6 +31,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -82,6 +83,7 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -530,6 +532,11 @@
      * certificate.</li>
      */
     private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
+        if (UserHandle.getAppId(mTaskFragmentOrganizerUid) == SYSTEM_UID) {
+            // The system is trusted to embed other apps securely and for all users.
+            return true;
+        }
+
         if (mTaskFragmentOrganizerUid == a.getUid()) {
             // Activities from the same UID can be embedded freely by the host.
             return true;
@@ -876,11 +883,17 @@
                 if (!adjacentTaskFragments.isEmpty() && !gotTranslucentAdjacent) {
                     // The z-order of this TaskFragment is in middle of two adjacent TaskFragments
                     // and it cannot be visible if the TaskFragment on top is not translucent and
-                    // is fully occluding this one.
+                    // is occluding this one.
+                    mTmpRect.set(getBounds());
                     for (int j = adjacentTaskFragments.size() - 1; j >= 0; --j) {
                         final TaskFragment taskFragment = adjacentTaskFragments.get(j);
-                        if (!taskFragment.isTranslucent(starting)
-                                && taskFragment.getBounds().contains(this.getBounds())) {
+                        final TaskFragment adjacentTaskFragment =
+                                taskFragment.mAdjacentTaskFragment;
+                        if (adjacentTaskFragment == this) {
+                            continue;
+                        }
+                        if (mTmpRect.intersect(taskFragment.getBounds())
+                                || mTmpRect.intersect(adjacentTaskFragment.getBounds())) {
                             return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
                         }
                     }
@@ -1695,7 +1708,7 @@
         if (isAddingActivity && task != null) {
 
             // TODO(b/207481538): temporary per-activity screenshoting
-            if (r != null && BackNavigationController.isEnabled()) {
+            if (r != null && BackNavigationController.isScreenshotEnabled()) {
                 ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
                         r.mActivityComponent.flattenToString());
                 Rect outBounds = r.getBounds();
@@ -2298,7 +2311,7 @@
 
     void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
         super.removeChild(child);
-        if (BackNavigationController.isEnabled()) {
+        if (BackNavigationController.isScreenshotEnabled()) {
             //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
             // implemented
             ActivityRecord r = child.asActivityRecord();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index a9add59..6a23eb5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -310,7 +310,7 @@
         }
         final ActivityRecord activity = result.first;
         final WindowState mainWindow = result.second;
-        final Rect contentInsets = getSystemBarInsets(task.getBounds(),
+        final Rect contentInsets = getSystemBarInsets(mainWindow.getFrame(),
                 mainWindow.getInsetsStateWithVisibilityOverride());
         final Rect letterboxInsets = activity.getLetterboxInsets();
         InsetUtils.addInsets(contentInsets, letterboxInsets);
@@ -575,7 +575,7 @@
         final LayoutParams attrs = mainWindow.getAttrs();
         final Rect taskBounds = task.getBounds();
         final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
-        final Rect systemBarInsets = getSystemBarInsets(taskBounds, insetsState);
+        final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
                 mHighResTaskSnapshotScale, insetsState);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 9f2188b..220381d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -52,6 +52,7 @@
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 
@@ -550,10 +551,10 @@
             throw new IllegalStateException("Too late to abort.");
         }
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Aborting Transition: %d", mSyncId);
-        mController.dispatchLegacyAppTransitionCancelled();
         mState = STATE_ABORT;
         // Syncengine abort will call through to onTransactionReady()
         mSyncEngine.abort(mSyncId);
+        mController.dispatchLegacyAppTransitionCancelled();
     }
 
     void setRemoteTransition(RemoteTransition remoteTransition) {
@@ -612,8 +613,6 @@
 
         handleNonAppWindowsInTransition(dc, mType, mFlags);
 
-        reportStartReasonsToLogger();
-
         // The callback is only populated for custom activity-level client animations
         sendRemoteCallback(mClientAnimationStartCallback);
 
@@ -702,6 +701,8 @@
         }
         mSyncId = -1;
         mOverrideOptions = null;
+
+        reportStartReasonsToLogger();
     }
 
     /**
@@ -894,11 +895,15 @@
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
             if (r == null || !r.mVisibleRequested) continue;
+            int transitionReason = APP_TRANSITION_WINDOWS_DRAWN;
             // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
             // ready due to starting-window.
-            reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
-                    && !r.mLastAllReadyAtSync)
-                    ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+            if (r.mStartingData instanceof SplashScreenStartingData && !r.mLastAllReadyAtSync) {
+                transitionReason = APP_TRANSITION_SPLASH_SCREEN;
+            } else if (r.isActivityTypeHomeOrRecents() && isTransientLaunch(r)) {
+                transitionReason = APP_TRANSITION_RECENTS_ANIM;
+            }
+            reasons.put(r, transitionReason);
         }
         mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                 reasons);
@@ -1251,6 +1256,17 @@
                 final ActivityRecord topMostActivity = task.getTopMostActivity();
                 change.setAllowEnterPip(topMostActivity != null
                         && topMostActivity.checkEnterPictureInPictureAppOpsState());
+                final ActivityRecord topRunningActivity = task.topRunningActivity();
+                if (topRunningActivity != null && task.mDisplayContent != null) {
+                    // If Activity is in fixed rotation, its will be applied with the next rotation,
+                    // when the Task is still in the previous rotation.
+                    final int taskRotation = task.getWindowConfiguration().getDisplayRotation();
+                    final int activityRotation = topRunningActivity.getWindowConfiguration()
+                            .getDisplayRotation();
+                    if (taskRotation != activityRotation) {
+                        change.setEndFixedRotation(activityRotation);
+                    }
+                }
             } else if ((info.mFlags & ChangeInfo.FLAG_SEAMLESS_ROTATION) != 0) {
                 change.setRotationAnimation(ROTATION_ANIMATION_SEAMLESS);
             }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e1746cc..bbc8462 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -57,6 +57,7 @@
 import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
 import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
 import static com.android.server.wm.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -81,8 +82,11 @@
 import android.util.Pools;
 import android.util.RotationUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
@@ -127,7 +131,8 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
+        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
+        InsetsControlTarget {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
@@ -145,6 +150,25 @@
     // onParentChanged() notification.
     boolean mReparenting;
 
+    /**
+     * Map of {@link InsetsState.InternalInsetsType} to the {@link InsetsSourceProvider} that
+     * provides local insets for all children of the current {@link WindowContainer}.
+     *
+     * Note that these InsetsSourceProviders are not part of the {@link InsetsStateController} and
+     * live here. These are supposed to provide insets only to the subtree of the current
+     * {@link WindowContainer}.
+     */
+    @Nullable
+    SparseArray<InsetsSourceProvider> mLocalInsetsSourceProviders = null;
+
+    @Nullable
+    protected InsetsSourceProvider mControllableInsetProvider;
+
+    /**
+     * The insets sources provided by this windowContainer.
+     */
+    protected SparseArray<InsetsSource> mProvidedInsetsSources = null;
+
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
     protected final WindowList<E> mChildren = new WindowList<E>();
@@ -329,6 +353,146 @@
         mSurfaceFreezer = new SurfaceFreezer(this, wms);
     }
 
+    /**
+     * Updates the {@link WindowState#mAboveInsetsState} and
+     * {@link WindowState#mMergedLocalInsetsSources} by visiting the entire hierarchy.
+     *
+     * {@link WindowState#mAboveInsetsState} is updated by visiting all the windows in z-order
+     * top-to-bottom manner and considering the {@link WindowContainer#mProvidedInsetsSources}
+     * provided by the {@link WindowState}s at the top.
+     * {@link WindowState#updateAboveInsetsState(InsetsState, SparseArray, ArraySet)} visits the
+     * IME container in the correct order to make sure the IME insets are passed correctly to the
+     * {@link WindowState}s below it.
+     *
+     * {@link WindowState#mMergedLocalInsetsSources} is updated by considering
+     * {@link WindowContainer#mLocalInsetsSourceProviders} provided by all the parents of the
+     * window.
+     * A given insetsType can be provided as a LocalInsetsSourceProvider only once in a
+     * Parent-to-leaf path.
+     *
+     * Examples: Please take a look at
+     * {@link WindowContainerTests#testAddLocalInsetsSourceProvider()}
+     * {@link
+     * WindowContainerTests#testAddLocalInsetsSourceProvider_windowSkippedIfProvidingOnParent()}
+     * {@link WindowContainerTests#testRemoveLocalInsetsSourceProvider()}.
+     *
+     * @param aboveInsetsState The InsetsState of all the Windows above the current container.
+     * @param localInsetsSourceProvidersFromParent The local InsetsSourceProviders provided by all
+     *                                             the parents in the hierarchy of the current
+     *                                             container.
+     * @param insetsChangedWindows The windows which the insets changed have changed for.
+     */
+    void updateAboveInsetsState(InsetsState aboveInsetsState,
+            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            ArraySet<WindowState> insetsChangedWindows) {
+        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+                localInsetsSourceProvidersFromParent;
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+                mergedLocalInsetsSourceProviders.put(
+                        mLocalInsetsSourceProviders.keyAt(i),
+                        mLocalInsetsSourceProviders.valueAt(i));
+            }
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).updateAboveInsetsState(aboveInsetsState,
+                    mergedLocalInsetsSourceProviders, insetsChangedWindows);
+        }
+    }
+
+    static <T> SparseArray<T> createShallowCopy(SparseArray<T> inputArray) {
+        SparseArray<T> copyOfInput = new SparseArray<>(inputArray.size());
+        for (int i = 0; i < inputArray.size(); i++) {
+            copyOfInput.append(inputArray.keyAt(i), inputArray.valueAt(i));
+        }
+        return copyOfInput;
+    }
+
+    /**
+     * Sets the given {@code providerFrame} as one of the insets provider for this window
+     * container. These insets are only passed to the subtree of the current WindowContainer.
+     * For a given WindowContainer-to-Leaf path, one insetsType can't be overridden more than once.
+     * If that happens, only the latest one will be chosen.
+     *
+     * @param providerFrame the frame that will act as one of the insets providers for this window
+     *                      container
+     * @param insetsTypes the insets type which the providerFrame provides
+     */
+    void addLocalRectInsetsSourceProvider(Rect providerFrame,
+            @InsetsState.InternalInsetsType int[] insetsTypes) {
+        if (insetsTypes == null || insetsTypes.length == 0) {
+            throw new IllegalArgumentException("Insets type not specified.");
+        }
+        if (mLocalInsetsSourceProviders == null) {
+            mLocalInsetsSourceProviders = new SparseArray<>();
+        }
+        for (int i = 0; i < insetsTypes.length; i++) {
+            InsetsSourceProvider insetsSourceProvider =
+                    mLocalInsetsSourceProviders.get(insetsTypes[i]);
+            if (insetsSourceProvider != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "The local insets provider for this type " + insetsTypes[i]
+                            + " already exists. Overwriting");
+                }
+            }
+            if (insetsSourceProvider == null
+                    || !(insetsSourceProvider instanceof RectInsetsSourceProvider)) {
+                insetsSourceProvider =
+                        new RectInsetsSourceProvider(
+                                new InsetsSource(insetsTypes[i]),
+                                mDisplayContent.getInsetsStateController(),
+                                mDisplayContent);
+                mLocalInsetsSourceProviders.put(insetsTypes[i], insetsSourceProvider);
+            }
+            ((RectInsetsSourceProvider) insetsSourceProvider).setRect(providerFrame);
+        }
+        mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+    }
+
+    void removeLocalInsetsSourceProvider(@InsetsState.InternalInsetsType int[] insetsTypes) {
+        if (insetsTypes == null || insetsTypes.length == 0) {
+            throw new IllegalArgumentException("Insets type not specified.");
+        }
+        if (mLocalInsetsSourceProviders == null) {
+            return;
+        }
+
+        for (int i = 0; i < insetsTypes.length; i++) {
+            InsetsSourceProvider insetsSourceProvider =
+                    mLocalInsetsSourceProviders.get(insetsTypes[i]);
+            if (insetsSourceProvider == null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Given insets type " + insetsTypes[i] + " doesn't have a "
+                            + "local insetsSourceProvider.");
+                }
+                continue;
+            }
+            mLocalInsetsSourceProviders.remove(insetsTypes[i]);
+        }
+        mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+    }
+
+    /**
+     * Sets an {@link InsetsSourceProvider} to be associated with this {@code WindowContainer},
+     * but only if the provider itself is controllable, as one window can be the provider of more
+     * than one inset type (i.e. gesture insets). If this {code WindowContainer} is controllable,
+     * all its animations must be controlled by its control target, and the visibility of this
+     * {code WindowContainer} should be taken account into the state of the control target.
+     *
+     * @param insetProvider the provider which should not be visible to the client.
+     * @see WindowState#getInsetsState()
+     */
+    void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
+        mControllableInsetProvider = insetProvider;
+    }
+
+    InsetsSourceProvider getControllableInsetProvider() {
+        return mControllableInsetProvider;
+    }
+
+
     @Override
     final protected WindowContainer getParent() {
         return mParent;
@@ -858,6 +1022,13 @@
         }
     }
 
+    public SparseArray<InsetsSource> getProvidedInsetsSources() {
+        if (mProvidedInsetsSources == null) {
+            mProvidedInsetsSources = new SparseArray<>();
+        }
+        return mProvidedInsetsSources;
+    }
+
     DisplayContent getDisplayContent() {
         return mDisplayContent;
     }
@@ -2962,6 +3133,16 @@
         scheduleAnimation();
     }
 
+    void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
+        outPoint.set(left, top);
+        final WindowContainer parentWindowContainer = getParent();
+        if (parentWindowContainer == null) {
+            return;
+        }
+        final Rect parentBounds = parentWindowContainer.getBounds();
+        outPoint.offset(-parentBounds.left, -parentBounds.top);
+    }
+
     void reassignLayer(Transaction t) {
         final WindowContainer parent = getParent();
         if (parent != null) {
@@ -3636,8 +3817,8 @@
                     new ArrayList<>(insetTypes.size());
 
             for (int insetType : insetTypes) {
-                InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController()
-                        .getSourceProvider(insetType);
+                WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
+                        .getInsetsStateController().getSourceProvider(insetType);
 
                 // Will apply it immediately to current leash and to all future inset animations
                 // until we disable it.
diff --git a/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
new file mode 100644
index 0000000..aa2e8f5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.InsetsSource;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
+ */
+class WindowContainerInsetsSourceProvider extends InsetsSourceProvider {
+    // TODO(b/218734524): Move the window container specific stuff from InsetsSourceProvider to
+    //  this class.
+
+    WindowContainerInsetsSourceProvider(InsetsSource source,
+            InsetsStateController stateController, DisplayContent displayContent) {
+        super(source, stateController, displayContent);
+    }
+}
+
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index af12c0b..709f885 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -200,6 +200,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.RemoteCallback;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -281,13 +282,14 @@
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 import android.window.ClientWindowFrames;
-import android.window.IOnFpsCallbackListener;
+import android.window.ITaskFpsCallback;
 import android.window.TaskSnapshot;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.internal.protolog.ProtoLogImpl;
@@ -310,6 +312,8 @@
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.File;
@@ -419,7 +423,7 @@
             "persist.wm.enable_remote_keyguard_animation";
 
     private static final int sEnableRemoteKeyguardAnimation =
-            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2);
+            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
 
     /**
      * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
@@ -458,6 +462,10 @@
 
     final private KeyguardDisableHandler mKeyguardDisableHandler;
 
+    private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners =
+            new RemoteCallbackList<>();
+    private boolean mDispatchedKeyguardLockedState = false;
+
     // VR Vr2d Display Id.
     int mVr2dDisplayId = INVALID_DISPLAY;
     boolean mVrModeEnabled = false;
@@ -1821,7 +1829,7 @@
 
             // This window doesn't have a frame yet. Don't let this window cause the insets change.
             displayContent.getInsetsStateController().updateAboveInsetsState(
-                    win, false /* notifyInsetsChanged */);
+                    false /* notifyInsetsChanged */);
 
             outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
             getInsetsSourceControls(win, outActiveControls);
@@ -3027,6 +3035,7 @@
     @Override
     public void onKeyguardShowingAndNotOccludedChanged() {
         mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
+        dispatchKeyguardLockedStateState();
     }
 
     @Override
@@ -3216,6 +3225,50 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    @Override
+    public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+        enforceSubscribeToKeyguardLockedStatePermission();
+        boolean registered = mKeyguardLockedStateListeners.register(listener);
+        if (!registered) {
+            Slog.w(TAG, "Failed to register listener: " + listener);
+        }
+    }
+
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    @Override
+    public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+        enforceSubscribeToKeyguardLockedStatePermission();
+        mKeyguardLockedStateListeners.unregister(listener);
+    }
+
+    private void enforceSubscribeToKeyguardLockedStatePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE,
+                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE
+                        + " permission required to read keyguard visibility");
+    }
+
+    private void dispatchKeyguardLockedStateState() {
+        mH.post(() -> {
+            final boolean isKeyguardLocked = mPolicy.isKeyguardShowing();
+            if (mDispatchedKeyguardLockedState == isKeyguardLocked) {
+                return;
+            }
+            final int n = mKeyguardLockedStateListeners.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged(
+                            isKeyguardLocked);
+                } catch (RemoteException e) {
+                    // Handled by the RemoteCallbackList.
+                }
+            }
+            mKeyguardLockedStateListeners.finishBroadcast();
+            mDispatchedKeyguardLockedState = isKeyguardLocked;
+        });
+    }
+
     @Override
     public void setSwitchingUser(boolean switching) {
         if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -6617,6 +6670,7 @@
         PriorityDump.dump(mPriorityDumper, fd, pw, args);
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         boolean dumpAll = false;
@@ -8870,7 +8924,7 @@
     @Override
     @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
     public void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
-            IOnFpsCallbackListener listener) {
+            ITaskFpsCallback callback) {
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
                 != PackageManager.PERMISSION_GRANTED) {
             final int pid = Binder.getCallingPid();
@@ -8882,12 +8936,12 @@
             throw new IllegalArgumentException("no task with taskId: " + taskId);
         }
 
-        mTaskFpsCallbackController.registerCallback(taskId, listener);
+        mTaskFpsCallbackController.registerListener(taskId, callback);
     }
 
     @Override
     @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
-    public void unregisterTaskFpsCallback(IOnFpsCallbackListener listener) {
+    public void unregisterTaskFpsCallback(ITaskFpsCallback callback) {
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
                 != PackageManager.PERMISSION_GRANTED) {
             final int pid = Binder.getCallingPid();
@@ -8895,7 +8949,7 @@
                     + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
         }
 
-        mTaskFpsCallbackController.unregisterCallback(listener);
+        mTaskFpsCallbackController.unregisterListener(callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index a967ea8..de87ab9 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -1167,6 +1167,10 @@
                 if (mRotationResolverService == null) {
                     mRotationResolverService = LocalServices.getService(
                             RotationResolverInternal.class);
+                    if (mRotationResolverService == null) {
+                        finalizeRotation(reportedRotation);
+                        return;
+                    }
                 }
 
                 String packageName = null;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e84a747..87ef09e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -30,6 +30,7 @@
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.ITYPE_INVALID;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.SurfaceControl.getGlobalTransaction;
@@ -213,6 +214,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
@@ -257,6 +259,8 @@
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -678,9 +682,9 @@
     final InsetsState mAboveInsetsState = new InsetsState();
 
     /**
-     * The insets sources provided by this window.
+     * The insets state of sources provided by the overrides set on any parent up the hierarchy.
      */
-    final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>();
+    SparseArray<InsetsSource> mMergedLocalInsetsSources = null;
 
     /**
      * Surface insets from the previous call to relayout(), used to track
@@ -736,7 +740,6 @@
      */
     private boolean mIsDimming = false;
 
-    private @Nullable InsetsSourceProvider mControllableInsetProvider;
     private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
 
     /**
@@ -1645,11 +1648,51 @@
     }
 
     /**
-     * Returns the insets state for the window. Its sources may be the copies with visibility
-     * modification according to the state of transient bars.
+     * See {@link WindowState#getInsetsState(boolean)}
      */
     InsetsState getInsetsState() {
-        return getDisplayContent().getInsetsPolicy().getInsetsForWindow(this);
+        return getInsetsState(false);
+    }
+
+    /**
+     * Returns the insets state for the window. Its sources may be the copies with visibility
+     * modification according to the state of transient bars.
+     * This is to get the insets for a window layout on the screen. If the window is not there, use
+     * the {@link InsetsPolicy#getInsetsForWindowMetrics} to get insets instead.
+     * @param includeTransient whether or not the transient types should be included in the
+     *                         insets state.
+     */
+    InsetsState getInsetsState(boolean includeTransient) {
+        final InsetsState rotatedState = mToken.getFixedRotationTransformInsetsState();
+        final InsetsPolicy insetsPolicy = getDisplayContent().getInsetsPolicy();
+        if (rotatedState != null) {
+            return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
+        }
+        final InsetsSourceProvider provider = getControllableInsetProvider();
+        final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
+                ? provider.getSource().getType() : ITYPE_INVALID;
+        final InsetsState rawInsetsState =
+                mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
+        final InsetsState insetsStateForWindow = insetsPolicy
+                .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
+                        getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
+        return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
+                includeTransient);
+    }
+
+    private InsetsState getMergedInsetsState() {
+        final InsetsState globalInsetsState = mAttrs.receiveInsetsIgnoringZOrder
+                ? getDisplayContent().getInsetsStateController().getRawInsetsState()
+                : mAboveInsetsState;
+        if (mMergedLocalInsetsSources == null) {
+            return globalInsetsState;
+        }
+
+        final InsetsState mergedInsetsState = new InsetsState(globalInsetsState);
+        for (int i = 0; i < mMergedLocalInsetsSources.size(); i++) {
+            mergedInsetsState.addSource(mMergedLocalInsetsSources.valueAt(i));
+        }
+        return mergedInsetsState;
     }
 
     /**
@@ -4178,6 +4221,7 @@
         proto.end(token);
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         pw.print(prefix + "mDisplayId=" + getDisplayId());
@@ -4700,6 +4744,58 @@
         return false;
     }
 
+    @Override
+    void updateAboveInsetsState(InsetsState aboveInsetsState,
+            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            ArraySet<WindowState> insetsChangedWindows) {
+        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+                localInsetsSourceProvidersFromParent;
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+                mergedLocalInsetsSourceProviders.put(
+                        mLocalInsetsSourceProviders.keyAt(i),
+                        mLocalInsetsSourceProviders.valueAt(i));
+            }
+        }
+        final SparseArray<InsetsSource> mergedLocalInsetsSourcesFromParent =
+                toInsetsSources(mergedLocalInsetsSourceProviders);
+
+        // Insets provided by the IME window can effect all the windows below it and hence it needs
+        // to be visited in the correct order. Because of which updateAboveInsetsState() can't be
+        // used here and instead forAllWindows() is used.
+        forAllWindows(w -> {
+            if (!w.mAboveInsetsState.equals(aboveInsetsState)) {
+                w.mAboveInsetsState.set(aboveInsetsState);
+                insetsChangedWindows.add(w);
+            }
+
+            if (!mergedLocalInsetsSourcesFromParent.contentEquals(w.mMergedLocalInsetsSources)) {
+                w.mMergedLocalInsetsSources = createShallowCopy(
+                        mergedLocalInsetsSourcesFromParent);
+                insetsChangedWindows.add(w);
+            }
+
+            final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+            if (providedSources != null) {
+                for (int i = providedSources.size() - 1; i >= 0; i--) {
+                    aboveInsetsState.addSource(providedSources.valueAt(i));
+                }
+            }
+        }, true /* traverseTopToBottom */);
+    }
+
+    private static SparseArray<InsetsSource> toInsetsSources(
+            SparseArray<InsetsSourceProvider> insetsSourceProviders) {
+        final SparseArray<InsetsSource> insetsSources = new SparseArray<>(
+                insetsSourceProviders.size());
+        for (int i = 0; i < insetsSourceProviders.size(); i++) {
+            insetsSources.append(insetsSourceProviders.keyAt(i),
+                    insetsSourceProviders.valueAt(i).getSource());
+        }
+        return insetsSources;
+    }
+
     private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
         // We want to consume the positive sublayer children first because they need to appear
         // above the parent, then this window (the parent), and then the negative sublayer children
@@ -5655,24 +5751,6 @@
         mWindowFrames.setContentChanged(false);
     }
 
-    /**
-     * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
-     * provider itself is controllable, as one window can be the provider of more than one inset
-     * type (i.e. gesture insets). If this window is controllable, all its animations must be
-     * controlled by its control target, and the visibility of this window should be taken account
-     * into the state of the control target.
-     *
-     * @param insetProvider the provider which should not be visible to the client.
-     * @see InsetsStateController#getInsetsForWindow(WindowState)
-     */
-    void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
-        mControllableInsetProvider = insetProvider;
-    }
-
-    InsetsSourceProvider getControllableInsetProvider() {
-        return mControllableInsetProvider;
-    }
-
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f398034..ccaa03a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
@@ -615,6 +616,12 @@
             getResolvedOverrideConfiguration().updateFrom(
                     mFixedRotationTransformState.mRotatedOverrideConfiguration);
         }
+        if (getTaskDisplayArea() == null) {
+            // We only defined behaviors of system windows in fullscreen mode, i.e. windows not
+            // contained in a task display area.
+            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
+                    WINDOWING_MODE_FULLSCREEN);
+        }
     }
 
     @Override
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 94bc22a..636ca41 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -57,6 +57,19 @@
 #define ASYNC_RECEIVED_WHILE_FROZEN (2)
 #define TXNS_PENDING_WHILE_FROZEN (4)
 
+#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)
+
+// Defines the maximum amount of VMAs we can send per process_madvise syscall.
+// Currently this is set to UIO_MAXIOV which is the maximum segments allowed by
+// iovec implementation used by process_madvise syscall
+#define MAX_VMAS_PER_COMPACTION UIO_MAXIOV
+
+// Maximum bytes that we can send per process_madvise syscall once this limit
+// is reached we split the remaining VMAs into another syscall. The MAX_RW_COUNT
+// limit is imposed by iovec implementation. However, if you want to use a smaller
+// limit, it has to be a page aligned value, otherwise, compaction would fail.
+#define MAX_BYTES_PER_COMPACTION MAX_RW_COUNT
+
 namespace android {
 
 static bool cancelRunningCompaction;
@@ -70,12 +83,12 @@
 }
 
 // Compacts a set of VMAs for pid using an madviseType accepted by process_madvise syscall
-// On success returns the total bytes that where compacted. On failure it returns
-// a negative error code from the standard linux error codes.
+// Returns the total bytes that where madvised.
+//
+// If any VMA fails compaction due to -EINVAL it will be skipped and continue.
+// However, if it fails for any other reason, it will bail out and forward the error
 static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
-    // UIO_MAXIOV is currently a small value and we might have more addresses
-    // we do multiple syscalls if we exceed its maximum
-    static struct iovec vmasToKernel[UIO_MAXIOV];
+    static struct iovec vmasToKernel[MAX_VMAS_PER_COMPACTION];
 
     if (vmas.empty()) {
         return 0;
@@ -89,33 +102,72 @@
     compactionInProgress = true;
     cancelRunningCompaction = false;
 
-    int64_t totalBytesCompacted = 0;
-    for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
-        if (CC_UNLIKELY(cancelRunningCompaction)) {
-            // There could be a significant delay betweenwhen a compaction
-            // is requested and when it is handled during this time
-            // our OOM adjust could have improved.
+    int64_t totalBytesProcessed = 0;
+
+    int64_t vmaOffset = 0;
+    for (int iVma = 0; iVma < vmas.size();) {
+        uint64_t bytesSentToCompact = 0;
+        int iVec = 0;
+        while (iVec < MAX_VMAS_PER_COMPACTION && iVma < vmas.size()) {
+            if (CC_UNLIKELY(cancelRunningCompaction)) {
+                // There could be a significant delay between when a compaction
+                // is requested and when it is handled during this time our
+                // OOM adjust could have improved.
+                LOG(DEBUG) << "Cancelled running compaction for " << pid;
+                break;
+            }
+
+            uint64_t vmaStart = vmas[iVma].start + vmaOffset;
+            uint64_t vmaSize = vmas[iVma].end - vmaStart;
+            if (vmaSize == 0) {
+                goto next_vma;
+            }
+            vmasToKernel[iVec].iov_base = (void*)vmaStart;
+            if (vmaSize > MAX_BYTES_PER_COMPACTION - bytesSentToCompact) {
+                // Exceeded the max bytes that could be sent, so clamp
+                // the end to avoid exceeding limit and issue compaction
+                vmaSize = MAX_BYTES_PER_COMPACTION - bytesSentToCompact;
+            }
+
+            vmasToKernel[iVec].iov_len = vmaSize;
+            bytesSentToCompact += vmaSize;
+            ++iVec;
+            if (bytesSentToCompact >= MAX_BYTES_PER_COMPACTION) {
+                // Ran out of bytes within iovec, dispatch compaction.
+                vmaOffset += vmaSize;
+                break;
+            }
+
+        next_vma:
+            // Finished current VMA, and have more bytes remaining
+            vmaOffset = 0;
+            ++iVma;
+        }
+
+        if (cancelRunningCompaction) {
             cancelRunningCompaction = false;
             break;
         }
-        int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
-        for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
-            vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
-            vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;
+
+        auto bytesProcessed = process_madvise(pidfd, vmasToKernel, iVec, madviseType, 0);
+
+        if (CC_UNLIKELY(bytesProcessed == -1)) {
+            if (errno == EINVAL) {
+                // This error is somewhat common due to an unevictable VMA if this is
+                // the case silently skip the bad VMA and continue compacting the rest.
+                continue;
+            } else {
+                // Forward irrecoverable errors and bail out compaction
+                compactionInProgress = false;
+                return -errno;
+            }
         }
 
-        auto bytesCompacted =
-                process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
-        if (CC_UNLIKELY(bytesCompacted == -1)) {
-            compactionInProgress = false;
-            return -errno;
-        }
-
-        totalBytesCompacted += bytesCompacted;
+        totalBytesProcessed += bytesProcessed;
     }
     compactionInProgress = false;
 
-    return totalBytesCompacted;
+    return totalBytesProcessed;
 }
 
 static int getFilePageAdvice(const Vma& vma) {
@@ -138,7 +190,12 @@
 }
 
 // Perform a full process compaction using process_madvise syscall
-// reading all filtering VMAs and filtering pages as specified by pageFilter
+// using the madvise behavior defined by vmaToAdviseFunc per VMA.
+//
+// Currently supported behaviors are MADV_COLD and MADV_PAGEOUT.
+//
+// Returns the total number of bytes compacted or forwards an
+// process_madvise error.
 static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
     ProcMemInfo meminfo(pid);
     std::vector<Vma> pageoutVmas, coldVmas;
diff --git a/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
index 0202306..0a60e0d 100644
--- a/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
+++ b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
@@ -99,7 +99,7 @@
 
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
-        {"nativeRegister", "(Landroid/window/IOnFpsCallbackListener;I)J", (void*)nativeRegister},
+        {"nativeRegister", "(Landroid/window/ITaskFpsCallback;I)J", (void*)nativeRegister},
         {"nativeUnregister", "(J)V", (void*)nativeUnregister}};
 
 } // namespace
@@ -113,7 +113,7 @@
     gCallbackClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
     gCallbackClassInfo.mDispatchOnFpsReported =
             env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
-                                   "(Landroid/window/IOnFpsCallbackListener;F)V");
+                                   "(Landroid/window/ITaskFpsCallback;F)V");
     return 0;
 }
 
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 be0ddc1..5b4febd 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -170,14 +170,6 @@
         </xs:restriction>
     </xs:simpleType>
 
-    <!-- Maps to DisplayDeviceConfig.INTERPOLATION_* values. -->
-    <xs:simpleType name="interpolation">
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="default"/>
-            <xs:enumeration value="linear"/>
-        </xs:restriction>
-    </xs:simpleType>
-
     <xs:complexType name="thermalThrottling">
         <xs:complexType>
             <xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
@@ -216,7 +208,8 @@
                 <xs:annotation name="final"/>
             </xs:element>
         </xs:sequence>
-        <xs:attribute name="interpolation" type="interpolation" use="optional"/>
+        <!-- valid value of interpolation if specified: linear -->
+        <xs:attribute name="interpolation" type="xs:string" use="optional"/>
     </xs:complexType>
 
     <xs:complexType name="point">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 2890d68..ba83c9f 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -108,17 +108,11 @@
     method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
   }
 
-  public enum Interpolation {
-    method public String getRawName();
-    enum_constant public static final com.android.server.display.config.Interpolation _default;
-    enum_constant public static final com.android.server.display.config.Interpolation linear;
-  }
-
   public class NitsMap {
     ctor public NitsMap();
-    method public com.android.server.display.config.Interpolation getInterpolation();
+    method public String getInterpolation();
     method @NonNull public final java.util.List<com.android.server.display.config.Point> getPoint();
-    method public void setInterpolation(com.android.server.display.config.Interpolation);
+    method public void setInterpolation(String);
   }
 
   public class Point {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 091d879..edef6d2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -31,20 +31,6 @@
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
 import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
-import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
-import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
-import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
-import static android.app.admin.DevicePolicyManager.CODE_HAS_DEVICE_OWNER;
-import static android.app.admin.DevicePolicyManager.CODE_HAS_PAIRED;
-import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
-import static android.app.admin.DevicePolicyManager.CODE_NONSYSTEM_USER_EXISTS;
-import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_OK;
-import static android.app.admin.DevicePolicyManager.CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
-import static android.app.admin.DevicePolicyManager.CODE_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
-import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
-import static android.app.admin.DevicePolicyManager.CODE_USER_SETUP_COMPLETED;
 import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
 import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
@@ -101,6 +87,20 @@
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
+import static android.app.admin.DevicePolicyManager.STATUS_ACCOUNTS_NOT_EMPTY;
+import static android.app.admin.DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
+import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
+import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
+import static android.app.admin.DevicePolicyManager.STATUS_OK;
+import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
+import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_HAS_PROFILE_OWNER;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_NOT_RUNNING;
+import static android.app.admin.DevicePolicyManager.STATUS_USER_SETUP_COMPLETED;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -1999,9 +1999,6 @@
             mOwners.load();
             setDeviceOwnershipSystemPropertyLocked();
             findOwnerComponentIfNecessaryLocked();
-
-            // TODO PO may not have a class name either due to b/17652534.  Address that too.
-            updateDeviceOwnerLocked();
         }
     }
 
@@ -3142,23 +3139,6 @@
         }
     }
 
-    private void updateDeviceOwnerLocked() {
-        long ident = mInjector.binderClearCallingIdentity();
-        try {
-            // TODO This is to prevent DO from getting "clear data"ed, but it should also check the
-            // user id and also protect all other DAs too.
-            final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
-            if (deviceOwnerComponent != null) {
-                mInjector.getIActivityManager()
-                        .updateDeviceOwner(deviceOwnerComponent.getPackageName());
-            }
-        } catch (RemoteException e) {
-            // Not gonna happen.
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
-        }
-    }
-
     static void validateQualityConstant(int quality) {
         switch (quality) {
             case PASSWORD_QUALITY_UNSPECIFIED:
@@ -7245,7 +7225,7 @@
             return;
         }
         Preconditions.checkCallAuthorization(
-                hasCallingOrSelfPermission(permission.SEND_LOST_MODE_LOCATION_UPDATES));
+                hasCallingOrSelfPermission(permission.TRIGGER_LOST_MODE));
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
@@ -8590,7 +8570,6 @@
 
             mOwners.setDeviceOwner(admin, ownerName, userId);
             mOwners.writeDeviceOwner();
-            updateDeviceOwnerLocked();
             setDeviceOwnershipSystemPropertyLocked();
 
             //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
@@ -8951,7 +8930,6 @@
 
         mOwners.clearDeviceOwner();
         mOwners.writeDeviceOwner();
-        updateDeviceOwnerLocked();
 
         clearDeviceOwnerUserRestriction(UserHandle.of(userId));
         mInjector.securityLogSetLoggingEnabledProperty(false);
@@ -9658,7 +9636,7 @@
         final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner,
                 /* deviceOwnerUserId= */ deviceOwnerUserId, /* callingUserId*/ caller.getUserId(),
                 isAdb(caller), hasIncompatibleAccountsOrNonAdb);
-        if (code != CODE_OK) {
+        if (code != STATUS_OK) {
             throw new IllegalStateException(
                     computeProvisioningErrorString(code, deviceOwnerUserId));
         }
@@ -9666,25 +9644,25 @@
 
     private static String computeProvisioningErrorString(int code, @UserIdInt int userId) {
         switch (code) {
-            case CODE_OK:
+            case STATUS_OK:
                 return "OK";
-            case CODE_HAS_DEVICE_OWNER:
+            case STATUS_HAS_DEVICE_OWNER:
                 return "Trying to set the device owner, but device owner is already set.";
-            case CODE_USER_HAS_PROFILE_OWNER:
+            case STATUS_USER_HAS_PROFILE_OWNER:
                 return "Trying to set the device owner, but the user already has a profile owner.";
-            case CODE_USER_NOT_RUNNING:
+            case STATUS_USER_NOT_RUNNING:
                 return "User " + userId + " not running.";
-            case CODE_NOT_SYSTEM_USER:
+            case STATUS_NOT_SYSTEM_USER:
                 return "User " + userId + " is not system user.";
-            case CODE_USER_SETUP_COMPLETED:
+            case STATUS_USER_SETUP_COMPLETED:
                 return  "Cannot set the device owner if the device is already set-up.";
-            case CODE_NONSYSTEM_USER_EXISTS:
+            case STATUS_NONSYSTEM_USER_EXISTS:
                 return "Not allowed to set the device owner because there are already several"
                         + " users on the device.";
-            case CODE_ACCOUNTS_NOT_EMPTY:
+            case STATUS_ACCOUNTS_NOT_EMPTY:
                 return "Not allowed to set the device owner because there are already some accounts"
                         + " on the device.";
-            case CODE_HAS_PAIRED:
+            case STATUS_HAS_PAIRED:
                 return "Not allowed to set the device owner because this device has already "
                         + "paired.";
             default:
@@ -14159,30 +14137,30 @@
             mInjector.binderRestoreCallingIdentity(ident);
         }
 
-        return checkProvisioningPreConditionSkipPermission(action, packageName) == CODE_OK;
+        return checkProvisioningPreconditionSkipPermission(action, packageName) == STATUS_OK;
     }
 
     @Override
-    public int checkProvisioningPreCondition(String action, String packageName) {
+    public int checkProvisioningPrecondition(String action, String packageName) {
         Objects.requireNonNull(packageName, "packageName is null");
 
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
 
-        return checkProvisioningPreConditionSkipPermission(action, packageName);
+        return checkProvisioningPreconditionSkipPermission(action, packageName);
     }
 
-    private int checkProvisioningPreConditionSkipPermission(String action,
+    private int checkProvisioningPreconditionSkipPermission(String action,
             String packageName) {
         if (!mHasFeature) {
             logMissingFeatureAction("Cannot check provisioning for action " + action);
-            return CODE_DEVICE_ADMIN_NOT_SUPPORTED;
+            return STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
         }
         if (!isProvisioningAllowed()) {
-            return CODE_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
+            return STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
         }
         final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
-        if (code != CODE_OK) {
+        if (code != STATUS_OK) {
             Slogf.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
                     + ") failed: "
                     + computeProvisioningErrorString(code, mInjector.userHandleGetCallingUserId()));
@@ -14230,27 +14208,27 @@
             @UserIdInt int deviceOwnerUserId, @UserIdInt int callingUserId, boolean isAdb,
             boolean hasIncompatibleAccountsOrNonAdb) {
         if (mOwners.hasDeviceOwner()) {
-            return CODE_HAS_DEVICE_OWNER;
+            return STATUS_HAS_DEVICE_OWNER;
         }
         if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
-            return CODE_USER_HAS_PROFILE_OWNER;
+            return STATUS_USER_HAS_PROFILE_OWNER;
         }
 
         boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
         // System user is always running in headless system user mode.
         if (!isHeadlessSystemUserMode
                 && !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
-            return CODE_USER_NOT_RUNNING;
+            return STATUS_USER_NOT_RUNNING;
         }
         if (mIsWatch && hasPaired(UserHandle.USER_SYSTEM)) {
-            return CODE_HAS_PAIRED;
+            return STATUS_HAS_PAIRED;
         }
 
         if (isHeadlessSystemUserMode) {
             if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
                 Slogf.e(LOG_TAG, "In headless system user mode, "
                         + "device owner can only be set on headless system user.");
-                return CODE_NOT_SYSTEM_USER;
+                return STATUS_NOT_SYSTEM_USER;
             }
         }
 
@@ -14265,7 +14243,7 @@
 
                 int maxNumberOfExistingUsers = isHeadlessSystemUserMode ? 2 : 1;
                 if (mUserManager.getUserCount() > maxNumberOfExistingUsers) {
-                    return CODE_NONSYSTEM_USER_EXISTS;
+                    return STATUS_NONSYSTEM_USER_EXISTS;
                 }
 
                 int currentForegroundUser = getCurrentForegroundUserId();
@@ -14274,23 +14252,23 @@
                         && currentForegroundUser == UserHandle.USER_SYSTEM) {
                     Slogf.wtf(LOG_TAG, "In headless system user mode, "
                             + "current user cannot be system user when setting device owner");
-                    return CODE_SYSTEM_USER;
+                    return STATUS_SYSTEM_USER;
                 }
                 if (hasIncompatibleAccountsOrNonAdb) {
-                    return CODE_ACCOUNTS_NOT_EMPTY;
+                    return STATUS_ACCOUNTS_NOT_EMPTY;
                 }
             }
-            return CODE_OK;
+            return STATUS_OK;
         } else {
             // DO has to be user 0
             if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
-                return CODE_NOT_SYSTEM_USER;
+                return STATUS_NOT_SYSTEM_USER;
             }
             // Only provision DO before setup wizard completes
             if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
-                return CODE_USER_SETUP_COMPLETED;
+                return STATUS_USER_SETUP_COMPLETED;
             }
-            return CODE_OK;
+            return STATUS_OK;
         }
     }
 
@@ -14311,11 +14289,11 @@
     private int checkManagedProfileProvisioningPreCondition(String packageName,
             @UserIdInt int callingUserId) {
         if (!hasFeatureManagedUsers()) {
-            return CODE_MANAGED_USERS_NOT_SUPPORTED;
+            return STATUS_MANAGED_USERS_NOT_SUPPORTED;
         }
         if (getProfileOwnerAsUser(callingUserId) != null) {
             // Managed user cannot have a managed profile.
-            return CODE_USER_HAS_PROFILE_OWNER;
+            return STATUS_USER_HAS_PROFILE_OWNER;
         }
 
         final long ident = mInjector.binderClearCallingIdentity();
@@ -14334,7 +14312,7 @@
                         callingUserId);
                 // The check is called from inside a managed profile. A managed profile cannot
                 // be provisioned from within another managed profile.
-                return CODE_CANNOT_ADD_MANAGED_PROFILE;
+                return STATUS_CANNOT_ADD_MANAGED_PROFILE;
             }
 
             // If there's a device owner, the restriction on adding a managed profile must be set.
@@ -14346,19 +14324,19 @@
             if (addingProfileRestricted) {
                 Slogf.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
                         callingUserHandle, hasDeviceOwner);
-                return CODE_CANNOT_ADD_MANAGED_PROFILE;
+                return STATUS_CANNOT_ADD_MANAGED_PROFILE;
             }
 
             // Bail out if we are trying to provision a work profile but one already exists.
             if (!mUserManager.canAddMoreManagedProfiles(
                     callingUserId, /* allowedToRemoveOne= */ false)) {
                 Slogf.i(LOG_TAG, "A work profile already exists.");
-                return CODE_CANNOT_ADD_MANAGED_PROFILE;
+                return STATUS_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
-        return CODE_OK;
+        return STATUS_OK;
     }
 
     private void checkIsDeviceOwner(CallerIdentity caller) {
@@ -17697,9 +17675,9 @@
         UserInfo userInfo = null;
         final long identity = Binder.clearCallingIdentity();
         try {
-            final int result = checkProvisioningPreConditionSkipPermission(
+            final int result = checkProvisioningPreconditionSkipPermission(
                     ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
-            if (result != CODE_OK) {
+            if (result != STATUS_OK) {
                 throw new ServiceSpecificException(
                         ERROR_PRE_CONDITION_FAILED,
                         "Provisioning preconditions failed with result: " + result);
@@ -18076,9 +18054,9 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            int result = checkProvisioningPreConditionSkipPermission(
+            int result = checkProvisioningPreconditionSkipPermission(
                     ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
-            if (result != CODE_OK) {
+            if (result != STATUS_OK) {
                 throw new ServiceSpecificException(
                         ERROR_PRE_CONDITION_FAILED,
                         "Provisioning preconditions failed with result: " + result);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5c3721d..e895d37 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -417,6 +417,8 @@
     private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
     private static final String SAFETY_CENTER_SERVICE_CLASS =
             "com.android.safetycenter.SafetyCenterService";
+    private static final String BLUETOOTH_SERVICE_CLASS =
+            "com.android.server.bluetooth.BluetoothService";
 
     private static final String SUPPLEMENTALPROCESS_SERVICE_CLASS =
             "com.android.server.supplementalprocess.SupplementalProcessManagerService$Lifecycle";
@@ -1624,7 +1626,7 @@
                 Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)");
             } else {
                 t.traceBegin("StartBluetoothService");
-                mSystemServiceManager.startService(BluetoothService.class);
+                mSystemServiceManager.startService(BLUETOOTH_SERVICE_CLASS);
                 t.traceEnd();
             }
 
@@ -2705,15 +2707,6 @@
             systemTheme.rebase();
         }
 
-        t.traceBegin("MakePowerManagerServiceReady");
-        try {
-            // TODO: use boot phase
-            mPowerManagerService.systemReady();
-        } catch (Throwable e) {
-            reportWtf("making Power Manager Service ready", e);
-        }
-        t.traceEnd();
-
         // Permission policy service
         t.traceBegin("StartPermissionPolicyService");
         mSystemServiceManager.startService(PermissionPolicyService.class);
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f72b23c..bcdbc5d 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -63,6 +63,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -128,6 +129,19 @@
 
     private final PackageManager mPackageManager;
 
+    private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
+    private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
+
+    // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse.
+    private final Object mUsbMidiLock = new Object();
+
+    // Number of times a USB MIDI 1.0 device has opened, based on the device name.
+    private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount =
+            new HashMap<String, Integer>();
+
+    // Whether a USB MIDI device has opened, based on the device name.
+    private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>();
+
     // UID of BluetoothMidiService
     private int mBluetoothServiceUid;
 
@@ -271,6 +285,12 @@
             }
 
             for (DeviceConnection connection : mDeviceConnections.values()) {
+                if (connection.getDevice().getDeviceInfo().getType()
+                        == MidiDeviceInfo.TYPE_USB) {
+                    synchronized (mUsbMidiLock) {
+                        removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                    }
+                }
                 connection.getDevice().removeDeviceConnection(connection);
             }
         }
@@ -784,6 +804,15 @@
             }
         }
 
+        if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
+            synchronized (mUsbMidiLock) {
+                if (isUsbMidiDeviceInUseLocked(deviceInfo)) {
+                    throw new IllegalArgumentException("device already in use: " + deviceInfo);
+                }
+                addUsbMidiDeviceLocked(deviceInfo);
+            }
+        }
+
         // clear calling identity so bindService does not fail
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1216,4 +1245,82 @@
         }
         pw.decreaseIndent();
     }
+
+    // hold mUsbMidiLock before calling this
+    private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return false;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        // Only one MIDI 2.0 device can be used at once.
+        // Multiple MIDI 1.0 devices can be used at once.
+        if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
+                || ((tagName).equals(MIDI_UNIVERSAL_STRING)
+                && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) {
+            return true;
+        }
+        return false;
+    }
+
+    // hold mUsbMidiLock before calling this
+    void addUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.add(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1;
+            mUsbMidiLegacyDeviceOpenCount.put(deviceName, count);
+        }
+    }
+
+    // hold mUsbMidiLock before calling this
+    void removeUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.remove(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) {
+                int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName);
+                if (count > 1) {
+                    mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1);
+                } else {
+                    mUsbMidiLegacyDeviceOpenCount.remove(deviceName);
+                }
+            }
+        }
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts out the "manufacturer product#Id " part.
+    // Two devices would have the same device name if they had the following property name:
+    // "manufacturer product#Id MIDI 1.0"
+    // "manufacturer product#Id MIDI 2.0"
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceName(String propertyName) {
+        return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts the "MIDI 1.0" part.
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceTag(String propertyName) {
+        return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
 }
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 c5709fc..dcc461b 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
@@ -29,6 +29,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
@@ -46,6 +47,8 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.intThat
 import org.mockito.Mockito.never
 import org.mockito.Mockito.same
@@ -262,12 +265,14 @@
         assertServiceInitialized() ?: return
         when (params.result) {
             is Result.Changed, Result.ChangedWithoutNotify -> {
-                assertThat(mockPendingBroadcasts.get(userId, params.pkgName) ?: emptyList<String>())
+                assertThat(mockPendingBroadcasts.copiedMap()?.get(userId)?.get(params.pkgName)
+                    ?: emptyList<String>())
                         .containsExactly(params.componentName!!.className)
                         .inOrder()
             }
             is Result.NotChanged, is Result.Exception -> {
-                assertThat(mockPendingBroadcasts.get(userId, params.pkgName)).isNull()
+                assertThat(mockPendingBroadcasts.copiedMap()?.get(userId)?.get(params.pkgName))
+                    .isNull()
             }
         }.run { /*exhaust*/ }
     }
@@ -337,8 +342,8 @@
         val mockSettings = Settings(mockedPkgSettings)
         val mockComponentResolver: ComponentResolver = mockThrowOnUnmocked {
             params.componentName?.let {
-                whenever(this.componentExists(same(it))) { mockActivity != null }
-                whenever(this.getActivity(same(it))) { mockActivity }
+                doReturn(mockActivity != null).`when`(this).componentExists(same(it))
+                doReturn(mockActivity).`when`(this).getActivity(same(it))
             }
             whenever(this.snapshot()) { this@mockThrowOnUnmocked }
             whenever(registerObserver(any())).thenCallRealMethod()
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 11300ce..83ccabf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -238,6 +238,7 @@
         AndroidPackage::isVendor,
         AndroidPackage::isVisibleToInstantApps,
         AndroidPackage::isVmSafeMode,
+        AndroidPackage::isLeavingSharedUid,
         AndroidPackage::isResetEnabledSettingsOnAppDataCleared,
         AndroidPackage::getMaxAspectRatio,
         AndroidPackage::getMinAspectRatio,
@@ -509,7 +510,12 @@
             ParsingPackage::setInheritKeyStoreKeys,
             true
         ),
-        getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
+        getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
+        getSetByValue(
+            AndroidPackage::isOnBackInvokedCallbackEnabled,
+            ParsingPackage::setOnBackInvokedCallbackEnabled,
+            true
+        )
     )
 
     override fun initialObject() = PackageImpl.forParsing(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 92cdb34..c9601de 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -20,8 +20,6 @@
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.SigningDetails
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.content.pm.verify.domain.DomainVerificationState
 import android.os.Build
@@ -30,10 +28,12 @@
 import android.util.IndentingPrintWriter
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.pm.Computer
 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.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationEnforcer
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
@@ -100,11 +100,15 @@
                     mockThrowOnUnmocked {
                         whenever(callingUid) { callingUidInt.get() }
                         whenever(callingUserId) { callingUserIdInt.get() }
-                        mockPackageStates {
-                            when (it) {
-                                VISIBLE_PKG -> visiblePkgState
-                                INVISIBLE_PKG -> invisiblePkgState
-                                else -> null
+                        whenever(snapshot()) {
+                            mockThrowOnUnmocked {
+                                whenever(getPackageStateInternal(anyString())) {
+                                    when (getArgument<String>(0)) {
+                                        VISIBLE_PKG -> visiblePkgState
+                                        INVISIBLE_PKG -> invisiblePkgState
+                                        else -> null
+                                    }
+                                }
                             }
                         }
                         whenever(schedule(anyInt(), any()))
@@ -211,9 +215,8 @@
                     printState(mock(IndentingPrintWriter::class.java), null, null)
                 },
                 service(Type.QUERENT, "printStateInternal") {
-                    printState(mock(IndentingPrintWriter::class.java), null, null) {
-                        mockPkgState(it, UUID.randomUUID())
-                    }
+                    printState(mock(Computer::class.java), mock(IndentingPrintWriter::class.java),
+                        null, null)
                 },
                 service(Type.VERIFIER, "setStatus") {
                     setDomainVerificationStatus(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 878bee0..7273b0b 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -19,9 +19,6 @@
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo
 import android.content.pm.verify.domain.DomainVerificationManager
@@ -34,7 +31,9 @@
 import android.util.SparseArray
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
+import com.android.server.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationManagerStub
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mockThrowOnUnmocked
@@ -502,8 +501,12 @@
                 whenever(callingUid) { Process.ROOT_UID }
                 whenever(callingUserId) { 0 }
 
-                mockPackageStates {
-                    pkgStateFunction(it)
+                whenever(snapshot()) {
+                    mockThrowOnUnmocked {
+                        whenever(getPackageStateInternal(anyString())) {
+                            pkgStateFunction(getArgument(0))
+                        }
+                    }
                 }
             })
         }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 0369bab..40f37a7 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -20,9 +20,6 @@
 import android.content.pm.PackageManager
 import android.content.pm.Signature
 import android.content.pm.SigningDetails
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.PackageUserStateInternal
 import android.content.pm.verify.domain.DomainOwner
 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
@@ -39,9 +36,12 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import android.util.Xml
+import com.android.server.pm.Computer
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
+import com.android.server.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mock
 import com.android.server.testutils.mockThrowOnUnmocked
@@ -194,7 +194,8 @@
         """
 
         val service = makeService(pkg1, pkg2)
-        service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
+        val computer = mockComputer(pkg1, pkg2)
+        service.restoreSettings(computer, Xml.resolvePullParser(xml.byteInputStream()))
         service.addPackage(pkg1)
         val info = service.getInfo(pkg1.packageName)
         assertThat(info.packageName).isEqualTo(pkg1.packageName)
@@ -243,7 +244,8 @@
         """
 
         val service = makeService(pkg1, pkg2)
-        service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
+        val computer = mockComputer(pkg1, pkg2)
+        service.restoreSettings(computer, Xml.resolvePullParser(xml.byteInputStream()))
         service.addPackage(pkg1)
         val info = service.getInfo(pkg1.packageName)
         assertThat(info.packageName).isEqualTo(pkg1.packageName)
@@ -298,8 +300,9 @@
         """.trimIndent()
 
         val service = makeService(pkg1, pkg2)
+        val computer = mockComputer(pkg1, pkg2)
         xml.byteInputStream().use {
-            service.readSettings(Xml.resolvePullParser(it))
+            service.readSettings(computer, Xml.resolvePullParser(it))
         }
 
         service.addPackage(pkg1)
@@ -311,8 +314,9 @@
     fun addPackagePendingStripInvalidDomains() {
         val xml = addPackagePendingOrRestoredWithInvalidDomains()
         val service = makeService(pkg1, pkg2)
+        val computer = mockComputer(pkg1, pkg2)
         xml.byteInputStream().use {
-            service.readSettings(Xml.resolvePullParser(it))
+            service.readSettings(computer, Xml.resolvePullParser(it))
         }
 
         service.addPackage(pkg1)
@@ -334,8 +338,9 @@
     fun addPackageRestoredStripInvalidDomains() {
         val xml = addPackagePendingOrRestoredWithInvalidDomains()
         val service = makeService(pkg1, pkg2)
+        val computer = mockComputer(pkg1, pkg2)
         xml.byteInputStream().use {
-            service.restoreSettings(Xml.resolvePullParser(it))
+            service.restoreSettings(computer, Xml.resolvePullParser(it))
         }
 
         service.addPackage(pkg1)
@@ -686,6 +691,7 @@
         val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, SIGNATURE_TWO,
             listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
         val serviceBefore = makeService(pkg1, pkg2)
+        val computerBefore = mockComputer(pkg1, pkg2)
         serviceBefore.addPackage(pkg1)
         serviceBefore.addPackage(pkg2)
 
@@ -729,16 +735,17 @@
         assertExpectedState(serviceBefore)
 
         val backupUser0 = ByteArrayOutputStream().use {
-            serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 0)
+            serviceBefore.writeSettings(computerBefore, Xml.resolveSerializer(it), true, 0)
             it.toByteArray()
         }
 
         val backupUser1 = ByteArrayOutputStream().use {
-            serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 10)
+            serviceBefore.writeSettings(computerBefore, Xml.resolveSerializer(it), true, 10)
             it.toByteArray()
         }
 
         val serviceAfter = makeService(pkg1, pkg2)
+        val computerAfter = mockComputer(pkg1, pkg2)
         serviceAfter.addPackage(pkg1)
         serviceAfter.addPackage(pkg2)
 
@@ -763,7 +770,7 @@
         }
 
         ByteArrayInputStream(backupUser1).use {
-            serviceAfter.restoreSettings(Xml.resolvePullParser(it))
+            serviceAfter.restoreSettings(computerAfter, Xml.resolvePullParser(it))
         }
 
         // Assert user 1 was restored
@@ -800,7 +807,7 @@
         )
 
         ByteArrayInputStream(backupUser0).use {
-            serviceAfter.restoreSettings(Xml.resolvePullParser(it))
+            serviceAfter.restoreSettings(computerAfter, Xml.resolvePullParser(it))
         }
 
         assertExpectedState(serviceAfter)
@@ -848,12 +855,20 @@
                 whenever(callingUid) { Process.ROOT_UID }
                 whenever(callingUserId) { 0 }
 
-                mockPackageStates {
-                    pkgStateFunction(it)
-                }
+                whenever(snapshot()) { mockComputer(pkgStateFunction) }
             })
         }
 
+    private fun mockComputer(vararg pkgStates: PackageStateInternal) =
+        mockComputer { pkgName -> pkgStates.find { pkgName == it.packageName } }
+
+    private fun mockComputer(pkgStateFunction: (String) -> PackageStateInternal? = { null }) =
+        mockThrowOnUnmocked<Computer> {
+            whenever(getPackageStateInternal(anyString())) {
+                pkgStateFunction(getArgument(0))
+            }
+        }
+
     private fun mockPkgState(
         pkgName: String,
         domainSetId: UUID,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 3a602a8..fc20c26 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -29,7 +29,6 @@
 import android.util.SparseArray
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
@@ -245,10 +244,14 @@
         mockThrowOnUnmocked {
             whenever(callingUid) { TEST_UID }
             whenever(callingUserId) { TEST_USER_ID }
-            mockPackageStates {
-                when (it) {
-                    TEST_PKG -> mockPkgState()
-                    else -> null
+            whenever(snapshot()) {
+                mockThrowOnUnmocked {
+                    whenever(getPackageStateInternal(anyString())) {
+                        when (getArgument<String>(0)) {
+                            TEST_PKG -> mockPkgState()
+                            else -> null
+                        }
+                    }
                 }
             }
             whenever(schedule(anyInt(), any()))
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
deleted file mode 100644
index c5f0eb1..0000000
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.test.verify.domain
-
-import com.android.internal.util.FunctionalUtils
-import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
-import com.android.server.testutils.whenever
-import org.mockito.ArgumentMatchers.any
-import java.util.function.Consumer
-import java.util.function.Function
-
-internal object DomainVerificationTestUtils {
-
-    @Suppress("UNCHECKED_CAST")
-    fun DomainVerificationManagerInternal.Connection.mockPackageStates(
-        block: (String) -> PackageStateInternal?
-    ) {
-        whenever(withPackageSettingsSnapshot(any())) {
-            (arguments[0] as Consumer<Function<String, PackageStateInternal?>>).accept { block(it) }
-        }
-        whenever(withPackageSettingsSnapshotReturning(any())) {
-            (arguments[0] as FunctionalUtils.ThrowingFunction<
-                    Function<String, PackageStateInternal?>, *>)
-                .apply { block(it) }
-        }
-        whenever(withPackageSettingsSnapshotThrowing<Exception>(any())) {
-            (arguments[0] as FunctionalUtils.ThrowingCheckedConsumer<
-                    Function<String, PackageStateInternal?>, *>)
-                .accept { block(it) }
-        }
-        whenever(withPackageSettingsSnapshotThrowing2<Exception, Exception>(any())) {
-            (arguments[0] as FunctionalUtils.ThrowingChecked2Consumer<
-                    Function<String, PackageStateInternal?>, *, *>)
-                .accept { block(it) }
-        }
-        whenever(withPackageSettingsSnapshotReturningThrowing<Any, Exception>(any())) {
-            (arguments[0] as FunctionalUtils.ThrowingCheckedFunction<
-                    Function<String, PackageStateInternal?>, *, *>)
-                .apply { block(it) }
-        }
-    }
-}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index ffc2877..589633c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -31,7 +31,6 @@
 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.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
 import com.android.server.pm.verify.domain.DomainVerificationService
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
@@ -85,11 +84,15 @@
                 // Need to provide an internal UID so some permission checks are ignored
                 whenever(callingUid) { Process.ROOT_UID }
                 whenever(callingUserId) { 0 }
-                mockPackageStates {
-                    when (it) {
-                        PKG_ONE -> pkg1
-                        PKG_TWO -> pkg2
-                        else -> null
+                whenever(snapshot()) {
+                    mockThrowOnUnmocked {
+                        whenever(getPackageStateInternal(anyString())) {
+                            when (getArgument<String>(0)) {
+                                PKG_ONE -> pkg1
+                                PKG_TWO -> pkg2
+                                else -> null
+                            }
+                        }
                     }
                 }
             })
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 936940f..e6bb0ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -2648,6 +2648,11 @@
         AppPermissionTracker getAppPermissionTracker() {
             return mAppPermissionTracker;
         }
+
+        @Override
+        void scheduleInitTrackers(Handler handler, Runnable initializers) {
+            initializers.run();
+        }
     }
 
     private class TestBaseTrackerInjector<T extends BaseAppStatePolicy>
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 d2358a0..023608c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -1167,7 +1167,14 @@
         startUser(gameManagerService, USER_ID_1);
         gameManagerService.setGameMode(
                 mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
-        GameState gameState = new GameState(isLoading, GameState.MODE_NONE);
+        int testMode = GameState.MODE_NONE;
+        int testLabel = 99;
+        int testQuality = 123;
+        GameState gameState = new GameState(isLoading, testMode, testLabel, testQuality);
+        assertEquals(isLoading, gameState.isLoading());
+        assertEquals(testMode, gameState.getMode());
+        assertEquals(testLabel, gameState.getLabel());
+        assertEquals(testQuality, gameState.getQuality());
         gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
         mTestLooper.dispatchAll();
         verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading);
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 1c480ee..03eff2a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceControllerTest.java
@@ -20,13 +20,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.net.Uri;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 
@@ -35,11 +40,13 @@
 
 import com.android.internal.util.ConcurrentUtils;
 import com.android.server.SystemService;
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -61,10 +68,14 @@
             new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceA");
     private static final ComponentName PROVIDER_A_SERVICE_B =
             new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceB");
+    private static final ComponentName PROVIDER_A_SERVICE_C =
+            new ComponentName(PROVIDER_A_PACKAGE_NAME, "com.provider.a.ServiceC");
 
     private MockitoSession mMockingSession;
     private GameServiceController mGameServiceManager;
     @Mock
+    private Context mMockContext;
+    @Mock
     private GameServiceProviderSelector mMockGameServiceProviderSelector;
     @Mock
     private GameServiceProviderInstanceFactory mMockGameServiceProviderInstanceFactory;
@@ -77,7 +88,7 @@
                 .startMocking();
 
         mGameServiceManager = new GameServiceController(
-                ConcurrentUtils.DIRECT_EXECUTOR,
+                mMockContext, ConcurrentUtils.DIRECT_EXECUTOR,
                 mMockGameServiceProviderSelector,
                 mMockGameServiceProviderInstanceFactory);
     }
@@ -96,25 +107,30 @@
 
     @Test
     public void notifyUserStarted_createsAndStartsNewInstance() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         FakeGameServiceProviderInstance instanceA =
                 seedConfigurationForUser(USER_10, configurationA);
 
         mGameServiceManager.onBootComplete();
         mGameServiceManager.notifyUserStarted(USER_10);
 
-        verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationA.getGameServiceComponentConfiguration());
         verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
         assertThat(instanceA.getIsRunning()).isTrue();
     }
 
     @Test
     public void notifyUserStarted_sameUser_doesNotCreateNewInstance() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         FakeGameServiceProviderInstance instanceA =
                 seedConfigurationForUser(USER_10, configurationA);
 
@@ -122,16 +138,19 @@
         mGameServiceManager.notifyUserStarted(USER_10);
         mGameServiceManager.notifyUserStarted(USER_10);
 
-        verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationA.getGameServiceComponentConfiguration());
         verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
         assertThat(instanceA.getIsRunning()).isTrue();
     }
 
     @Test
     public void notifyUserUnlocking_noForegroundUser_ignores() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         FakeGameServiceProviderInstance instanceA =
                 seedConfigurationForUser(USER_10, configurationA);
 
@@ -144,9 +163,11 @@
 
     @Test
     public void notifyUserUnlocking_sameAsForegroundUser_evaluatesProvider() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         seedNoConfigurationForUser(USER_10);
 
         mGameServiceManager.onBootComplete();
@@ -155,16 +176,19 @@
                 seedConfigurationForUser(USER_10, configurationA);
         mGameServiceManager.notifyUserUnlocking(USER_10);
 
-        verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationA.getGameServiceComponentConfiguration());
         verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
         assertThat(instanceA.getIsRunning()).isTrue();
     }
 
     @Test
     public void notifyUserUnlocking_differentFromForegroundUser_ignores() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         seedNoConfigurationForUser(USER_10);
 
         mGameServiceManager.onBootComplete();
@@ -180,14 +204,18 @@
     @Test
     public void
             notifyNewForegroundUser_differentUser_stopsPreviousInstanceAndThenStartsNewInstance() {
-        GameServiceProviderConfiguration configurationA =
-                new GameServiceProviderConfiguration(USER_HANDLE_10, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         FakeGameServiceProviderInstance instanceA =
                 seedConfigurationForUser(USER_10, configurationA);
-        GameServiceProviderConfiguration configurationB =
-                new GameServiceProviderConfiguration(USER_HANDLE_11, PROVIDER_A_SERVICE_A,
-                        PROVIDER_A_SERVICE_B);
+        GameServiceConfiguration configurationB =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_11,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
         FakeGameServiceProviderInstance instanceB = seedConfigurationForUser(USER_11,
                 configurationB);
         InOrder instancesInOrder = Mockito.inOrder(instanceA, instanceB);
@@ -196,8 +224,50 @@
         mGameServiceManager.notifyUserStarted(USER_10);
         mGameServiceManager.notifyNewForegroundUser(USER_11);
 
-        verify(mMockGameServiceProviderInstanceFactory).create(configurationA);
-        verify(mMockGameServiceProviderInstanceFactory).create(configurationB);
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationA.getGameServiceComponentConfiguration());
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationB.getGameServiceComponentConfiguration());
+        instancesInOrder.verify(instanceA).start();
+        instancesInOrder.verify(instanceA).stop();
+        instancesInOrder.verify(instanceB).start();
+        verifyNoMoreInteractions(mMockGameServiceProviderInstanceFactory);
+        assertThat(instanceA.getIsRunning()).isFalse();
+        assertThat(instanceB.getIsRunning()).isTrue();
+    }
+
+    @Test
+    public void packageChanges_reevaluatesGameServiceProvider() {
+        GameServiceConfiguration configurationA =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_B));
+        FakeGameServiceProviderInstance instanceA =
+                seedConfigurationForUser(USER_10, configurationA);
+
+        mGameServiceManager.onBootComplete();
+        mGameServiceManager.notifyUserStarted(USER_10);
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverArgumentCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext).registerReceiver(broadcastReceiverArgumentCaptor.capture(), any());
+
+        GameServiceConfiguration configurationB =
+                new GameServiceConfiguration(PROVIDER_A_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                PROVIDER_A_SERVICE_A,
+                                PROVIDER_A_SERVICE_C));
+        FakeGameServiceProviderInstance instanceB =
+                seedConfigurationForUser(USER_10, configurationA);
+        Intent intent = new Intent();
+        intent.setData(Uri.parse("package:" + PROVIDER_A_PACKAGE_NAME));
+        broadcastReceiverArgumentCaptor.getValue().onReceive(mMockContext, intent);
+
+        InOrder instancesInOrder = Mockito.inOrder(instanceA, instanceB);
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationA.getGameServiceComponentConfiguration());
+        verify(mMockGameServiceProviderInstanceFactory).create(
+                configurationB.getGameServiceComponentConfiguration());
         instancesInOrder.verify(instanceA).start();
         instancesInOrder.verify(instanceA).stop();
         instancesInOrder.verify(instanceB).start();
@@ -207,15 +277,16 @@
     }
 
     private void seedNoConfigurationForUser(SystemService.TargetUser user) {
-        when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(null);
+        when(mMockGameServiceProviderSelector.get(user, null)).thenReturn(null);
     }
 
     private FakeGameServiceProviderInstance seedConfigurationForUser(SystemService.TargetUser user,
-            GameServiceProviderConfiguration configuration) {
-        when(mMockGameServiceProviderSelector.get(user, "")).thenReturn(configuration);
+            GameServiceConfiguration configuration) {
+        when(mMockGameServiceProviderSelector.get(user, null)).thenReturn(configuration);
         FakeGameServiceProviderInstance instanceForConfiguration =
                 spy(new FakeGameServiceProviderInstance());
-        when(mMockGameServiceProviderInstanceFactory.create(configuration))
+        when(mMockGameServiceProviderInstanceFactory.create(
+                configuration.getGameServiceComponentConfiguration()))
                 .thenReturn(instanceForConfiguration);
 
         return instanceForConfiguration;
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 23a6a49..cf9ba1e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderSelectorImplTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -26,7 +27,6 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -49,6 +49,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.SystemService;
+import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration;
 
 import com.google.common.collect.ImmutableList;
 
@@ -137,10 +138,10 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(null, null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isNull();
     }
 
     @Test
@@ -154,15 +155,16 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(managedTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isNull();
     }
 
     @Test
     public void get_noSystemGameService_returnsNull()
             throws Exception {
+        seedSystemGameServicePackageName("");
         seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
                 resolveInfo(GAME_SERVICE_SERVICE_INFO));
         seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
@@ -170,14 +172,14 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isNull();
     }
 
     @Test
-    public void get_noGameServiceProvidersAvailable_returnsNull()
+    public void get_noGameServiceProvidersAvailable_returnsGameServicePackageName()
             throws Exception {
         seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
         seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10);
@@ -186,28 +188,30 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isEqualTo(
+                new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null));
     }
 
     @Test
-    public void get_gameServiceProviderHasNoMetaData_returnsNull()
+    public void get_gameServiceProviderHasNoMetaData_returnsGameServicePackageName()
             throws Exception {
         seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
         seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
                 resolveInfo(GAME_SERVICE_SERVICE_INFO_WITHOUT_META_DATA));
         seedServiceServiceInfo(GAME_SESSION_SERVICE_COMPONENT);
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isEqualTo(
+                new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null));
     }
 
     @Test
-    public void get_gameSessionServiceDoesNotExist_returnsNull()
+    public void get_gameSessionServiceDoesNotExist_returnsGameServicePackageName()
             throws Exception {
         seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
         seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
@@ -217,14 +221,15 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isEqualTo(
+                new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null));
     }
 
     @Test
-    public void get_metaDataWrongFirstTag_returnsNull() throws Exception {
+    public void get_metaDataWrongFirstTag_returnsGameServicePackageName() throws Exception {
         seedSystemGameServicePackageName(GAME_SERVICE_PACKAGE_NAME);
         seedGameServiceResolveInfos(GAME_SERVICE_PACKAGE_NAME, USER_HANDLE_10,
                 resolveInfo(GAME_SERVICE_SERVICE_INFO));
@@ -233,10 +238,11 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_wrong_first_tag.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        assertThat(gameServiceProviderConfiguration).isNull();
+        assertThat(gameServiceConfiguration).isEqualTo(
+                new GameServiceConfiguration(GAME_SERVICE_PACKAGE_NAME, null));
     }
 
     @Test
@@ -250,15 +256,17 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 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);
+        GameServiceConfiguration expectedGameServiceConfiguration =
+                new GameServiceConfiguration(
+                        GAME_SERVICE_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                GAME_SERVICE_COMPONENT,
+                                GAME_SESSION_SERVICE_COMPONENT));
+        assertThat(gameServiceConfiguration).isEqualTo(
+                expectedGameServiceConfiguration);
     }
 
     @Test
@@ -276,15 +284,17 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10), null);
 
-        GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
-                new GameServiceProviderConfiguration(USER_HANDLE_10,
-                        GAME_SERVICE_B_COMPONENT,
-                        GAME_SESSION_SERVICE_COMPONENT);
-        assertThat(gameServiceProviderConfiguration).isEqualTo(
-                expectedGameServiceProviderConfiguration);
+        GameServiceConfiguration expectedGameServiceConfiguration =
+                new GameServiceConfiguration(
+                        GAME_SERVICE_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                GAME_SERVICE_B_COMPONENT,
+                                GAME_SESSION_SERVICE_COMPONENT));
+        assertThat(gameServiceConfiguration).isEqualTo(
+                expectedGameServiceConfiguration);
     }
 
     @Test
@@ -299,15 +309,17 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 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);
+        GameServiceConfiguration expectedGameServiceConfiguration =
+                new GameServiceConfiguration(
+                        GAME_SERVICE_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(USER_HANDLE_10,
+                                GAME_SERVICE_COMPONENT,
+                                GAME_SESSION_SERVICE_COMPONENT));
+        assertThat(gameServiceConfiguration).isEqualTo(
+                expectedGameServiceConfiguration);
     }
 
     @Test
@@ -322,16 +334,19 @@
                 GAME_SERVICE_META_DATA_RES_ID,
                 "res/xml/game_service_metadata_valid.xml");
 
-        GameServiceProviderConfiguration gameServiceProviderConfiguration =
+        GameServiceConfiguration gameServiceConfiguration =
                 mGameServiceProviderSelector.get(eligibleTargetUser(USER_HANDLE_10),
                         GAME_SERVICE_PACKAGE_NAME);
 
-        GameServiceProviderConfiguration expectedGameServiceProviderConfiguration =
-                new GameServiceProviderConfiguration(USER_HANDLE_10,
-                        GAME_SERVICE_COMPONENT,
-                        GAME_SESSION_SERVICE_COMPONENT);
-        assertThat(gameServiceProviderConfiguration).isEqualTo(
-                expectedGameServiceProviderConfiguration);
+        GameServiceConfiguration expectedGameServiceConfiguration =
+                new GameServiceConfiguration(
+                        GAME_SERVICE_PACKAGE_NAME,
+                        new GameServiceComponentConfiguration(
+                                USER_HANDLE_10,
+                                GAME_SERVICE_COMPONENT,
+                                GAME_SESSION_SERVICE_COMPONENT));
+        assertThat(gameServiceConfiguration).isEqualTo(
+                expectedGameServiceConfiguration);
     }
 
     private void seedSystemGameServicePackageName(String gameServicePackageName) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 890a549..4e4854c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -333,6 +333,10 @@
 
     @Test
     public void testGetLastLocation_Bypass() {
+        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+                new PackageTagsList.Builder().add(
+                        IDENTITY.getPackageName()).build());
+
         assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
                 PERMISSION_FINE)).isNull();
         assertThat(mManager.getLastLocation(
@@ -381,6 +385,14 @@
                 new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
                 IDENTITY, PERMISSION_FINE)).isEqualTo(
                 loc);
+
+        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+                new PackageTagsList.Builder().build());
+        mProvider.setProviderAllowed(false);
+
+        assertThat(mManager.getLastLocation(
+                new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
+                IDENTITY, PERMISSION_FINE)).isNull();
     }
 
     @Test
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 4ec1641..6510cd1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -68,6 +68,7 @@
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.pkg.parsing.ParsingPackage
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
 import com.android.server.testutils.TestHandler
@@ -166,7 +167,7 @@
         }
         whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap)
         whenever(mocks.settings.getPackageLPr(anyString())) { mSettingsMap[getArgument<Any>(0)] }
-        whenever(mocks.settings.readLPw(nullable())) {
+        whenever(mocks.settings.readLPw(any(), nullable())) {
             mSettingsMap.putAll(mPreExistingSettings)
             !mPreExistingSettings.isEmpty()
         }
@@ -632,7 +633,7 @@
     }
 
     private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
-        whenever(mocks.componentResolver.queryActivities(
+        whenever(mocks.componentResolver.queryActivities(any(),
                 argThat { intent: Intent? -> intent != null && (action == intent.action) },
                 nullable(), anyLong(), anyInt())) {
             ArrayList(activities.asList().map { info: ActivityInfo? ->
@@ -642,7 +643,7 @@
     }
 
     private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
-        whenever(mocks.componentResolver.queryServices(
+        whenever(mocks.componentResolver.queryServices(any(),
                 argThat { intent: Intent? -> intent != null && (action == intent.action) },
                 nullable(), anyLong(), anyInt())) {
             ArrayList(services.asList().map { info ->
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 f35986b..b063d22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -120,17 +120,16 @@
         whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
         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())
+            mockThrowOnUnmocked<Computer> {
+                whenever(sharedLibraries) { mSharedLibrariesImpl.sharedLibraries }
+                whenever(resolveInternalPackageName(anyString(), anyLong())) {
+                    mPms.resolveInternalPackageName(getArgument(0), getArgument(1))
+                }
+                whenever(getPackageStateInternal(anyString())) {
+                    mPms.getPackageStateInternal(getArgument(0))
+                }
+            }
+        }.`when`(mPms).snapshotComputer()
         whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
             .thenReturn(PackageManager.DELETE_SUCCEEDED)
         whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
@@ -206,7 +205,8 @@
 
     @Test
     fun pruneUnusedStaticSharedLibraries() {
-        mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE, 0)
+        mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(mPms.snapshotComputer(),
+            Long.MAX_VALUE, 0)
 
         verify(mDeletePackageHelper)
             .deletePackageX(eq(STATIC_LIB_PACKAGE_NAME), any(), any(), any(), any())
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index f7b1dd5..1464405 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -39,8 +39,7 @@
 import android.content.pm.ApexStagedEvent;
 import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
+import android.content.pm.PackageManager;
 import android.content.pm.StagedApexInfo;
 import android.os.SystemProperties;
 import android.os.storage.IStorageManager;
@@ -158,10 +157,10 @@
 
         mStagingManager.restoreSessions(Arrays.asList(session1, session2), true);
 
-        assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+        assertThat(session1.getErrorCode()).isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed");
 
-        assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+        assertThat(session2.getErrorCode()).isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed");
     }
 
@@ -247,12 +246,12 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a "
                 + "staged session supposed to be activated");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -303,22 +302,22 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession1.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. "
                 + "Error: Failed for test");
 
         assertThat(apexSession2.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't "
                 + "activate nor fail. Marking it as failed anyway.");
 
         assertThat(apexSession3.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a "
                 + "staged session supposed to be activated");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -351,12 +350,12 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't "
                 + "activate nor fail. Marking it as failed anyway.");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -445,11 +444,11 @@
         verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
 
         assertThat(apexSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state");
 
         assertThat(apkSession.getErrorCode())
-                .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED);
+                .isEqualTo(PackageManager.INSTALL_ACTIVATION_FAILED);
         assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
     }
 
@@ -755,7 +754,7 @@
                 /* isReady */ false,
                 /* isFailed */ false,
                 /* isApplied */false,
-                /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.SESSION_NO_ERROR,
+                /* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN,
                 /* stagedSessionErrorMessage */ "no error");
 
         StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
@@ -775,7 +774,7 @@
         private boolean mIsReady = false;
         private boolean mIsApplied = false;
         private boolean mIsFailed = false;
-        private @SessionErrorCode int mErrorCode = -1;
+        private int mErrorCode = -1;
         private String mErrorMessage;
         private boolean mIsDestroyed = false;
         private int mParentSessionId = -1;
@@ -828,7 +827,7 @@
             return this;
         }
 
-        private @SessionErrorCode int getErrorCode() {
+        private int getErrorCode() {
             return mErrorCode;
         }
 
@@ -940,7 +939,7 @@
         }
 
         @Override
-        public void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage) {
+        public void setSessionFailed(int errorCode, String errorMessage) {
             Preconditions.checkState(!mIsApplied, "Already marked as applied");
             mIsFailed = true;
             mErrorCode = errorCode;
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 ac406b5..5230ea7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -123,8 +123,8 @@
     @Test
     fun setPackagesSuspended() {
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        val failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            targetPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
 
@@ -144,14 +144,15 @@
 
     @Test
     fun setPackagesSuspended_emptyPackageName() {
-        var failedNames = suspendPackageHelper.setPackagesSuspended(null /* packageNames */,
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            null /* packageNames */, true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+            deviceOwnerUid)
 
         assertThat(failedNames).isNull()
 
-        failedNames = suspendPackageHelper.setPackagesSuspended(arrayOfNulls(0),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+        failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOfNulls(0), true /* suspended */, null /* appExtras */, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
 
         assertThat(failedNames).isEmpty()
@@ -159,9 +160,10 @@
 
     @Test
     fun setPackagesSuspended_callerIsNotAllowed() {
-        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, Binder.getCallingUid())
+        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
+            Binder.getCallingUid())
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
@@ -169,9 +171,10 @@
 
     @Test
     fun setPackagesSuspended_callerSuspendItself() {
-        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(DEVICE_OWNER_PACKAGE),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+            deviceOwnerUid)
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
@@ -179,9 +182,10 @@
 
     @Test
     fun setPackagesSuspended_nonexistentPackage() {
-        val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(NONEXISTENT_PACKAGE),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+            deviceOwnerUid)
 
         assertThat(failedNames).asList().hasSize(1)
         assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
@@ -191,8 +195,8 @@
     fun setPackagesSuspended_knownPackages() {
         val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
             INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
-        val failedNames = suspendPackageHelper.setPackagesSuspended(knownPackages,
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+        val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            knownPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
 
         assertThat(failedNames.size).isEqualTo(knownPackages.size)
@@ -204,13 +208,13 @@
     @Test
     fun setPackagesUnsuspended() {
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            targetPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
-        failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
-            false /* suspended */, null /* appExtras */, null /* launcherExtras */,
+        failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            targetPackages, false /* suspended */, null /* appExtras */, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
 
@@ -235,7 +239,7 @@
         val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
         val unsuspendables = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
             INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
-        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(pms.snapshotComputer(),
             suspendables + unsuspendables, TEST_USER_ID, deviceOwnerUid)
 
         assertThat(results.size).isEqualTo(unsuspendables.size)
@@ -247,7 +251,7 @@
     @Test
     fun getUnsuspendablePackagesForUser_callerIsNotAllowed() {
         val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+        val results = suspendPackageHelper.getUnsuspendablePackagesForUser(pms.snapshotComputer(),
             suspendables, TEST_USER_ID, Binder.getCallingUid())
 
         assertThat(results.size).isEqualTo(suspendables.size)
@@ -260,8 +264,8 @@
     fun getSuspendedPackageAppExtras() {
         val appExtras = PersistableBundle()
         appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1)
-        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
-            true /* suspended */, appExtras, null /* launcherExtras */,
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -277,8 +281,8 @@
         val appExtras = PersistableBundle()
         appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_2)
         val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
-            true /* suspended */, appExtras, null /* launcherExtras */,
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -291,8 +295,9 @@
         assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
 
-        suspendPackageHelper.removeSuspensionsBySuspendingPackage(targetPackages,
-            { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID)
+        suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
+            targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE },
+            TEST_USER_ID)
 
         testHandler.flush()
         verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -320,8 +325,8 @@
     fun getSuspendedPackageLauncherExtras() {
         val launcherExtras = PersistableBundle()
         launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
-        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
-            true /* suspended */, null /* appExtras */, launcherExtras,
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -334,9 +339,10 @@
 
     @Test
     fun isPackageSuspended() {
-        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+            deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
@@ -348,8 +354,8 @@
     fun getSuspendingPackage() {
         val launcherExtras = PersistableBundle()
         launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
-        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
-            true /* suspended */, null /* appExtras */, launcherExtras,
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
             null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
@@ -362,9 +368,10 @@
     fun getSuspendedDialogInfo() {
         val dialogInfo = SuspendDialogInfo.Builder()
             .setTitle(TEST_PACKAGE_1).build()
-        var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
-            true /* suspended */, null /* appExtras */, null /* launcherExtras */,
-            dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+        var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
+            arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
+            null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
+            deviceOwnerUid)
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index 0411b94..9cf6c03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -263,7 +263,7 @@
     @Test
     public void testUserActivityOnDeviceStateChange() {
         createService();
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         final DisplayInfo info = new DisplayInfo();
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 64e8613..8b7cc74 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -18,8 +18,8 @@
 
 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 android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -102,106 +102,106 @@
     public void testMigration1() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, CAMERA));
 
-        assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState migrateFromFile(String fileName) throws IOException {
@@ -233,32 +233,32 @@
     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());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
     }
 
     @Test
     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());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_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));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
@@ -296,13 +296,15 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
-            SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
+            SensorState camState = sensorPrivacyStateController.getState(TOGGLE_TYPE_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);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA);
         } finally {
             mockitoSession.finishMocking();
         }
@@ -319,14 +321,16 @@
             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(sensorState).when(persistedState).getState(TOGGLE_TYPE_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);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
 
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
             assertEquals(0L, micState.getLastChange());
@@ -349,13 +353,13 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+            sensorPrivacyStateController.setState(TOGGLE_TYPE_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());
+            verify(persistedState, times(1)).setState(eq(TOGGLE_TYPE_SOFTWARE), eq(0),
+                    eq(MICROPHONE), captor.capture());
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
         } finally {
             mockitoSession.finishMocking();
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
index 6751b80..41d46f2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -73,6 +73,7 @@
                 .startMocking();
         when(mIrs.getContext()).thenReturn(mContext);
         when(mIrs.getCompleteEconomicPolicyLocked()).thenReturn(mEconomicPolicy);
+        when(mIrs.getLock()).thenReturn(mIrs);
         when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
         mScribe = new MockScribe(mIrs);
     }
@@ -89,75 +90,75 @@
         Agent agent = new Agent(mIrs, mScribe);
         Ledger ledger = new Ledger();
 
-        doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+        doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
         doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
 
-        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(5, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1000, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(500, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 500);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1_000_000L, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L, 1000);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(-1, ledger.getCurrentBalance());
     }
 
     @Test
-    public void testRecordTransaction_MaxCirculation() {
+    public void testRecordTransaction_MaxConsumptionLimit() {
         Agent agent = new Agent(mIrs, mScribe);
         Ledger ledger = new Ledger();
 
-        doReturn(1000L).when(mIrs).getMaxCirculationLocked();
-        doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
+        doReturn(1000L).when(mIrs).getConsumptionLimitLocked();
+        doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance();
 
-        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(5, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1000, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(500, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 2000);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 2000, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
-        assertEquals(1000, ledger.getCurrentBalance());
+        assertEquals(2500, ledger.getCurrentBalance());
 
-        // MaxCirculation can change as the battery level changes. Any already allocated ARCSs
-        // shouldn't be removed by recordTransaction().
-        doReturn(900L).when(mIrs).getMaxCirculationLocked();
+        // ConsumptionLimit can change as the battery level changes. Ledger balances shouldn't be
+        // affected.
+        doReturn(900L).when(mIrs).getConsumptionLimitLocked();
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
-        assertEquals(1000, ledger.getCurrentBalance());
+        assertEquals(2600, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -50);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -50, 50);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
-        assertEquals(950, ledger.getCurrentBalance());
+        assertEquals(2550, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -200);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -200, 100);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
-        assertEquals(750, ledger.getCurrentBalance());
+        assertEquals(2350, ledger.getCurrentBalance());
 
-        doReturn(800L).when(mIrs).getMaxCirculationLocked();
+        doReturn(800L).when(mIrs).getConsumptionLimitLocked();
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 100);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
-        assertEquals(800, ledger.getCurrentBalance());
+        assertEquals(2450, ledger.getCurrentBalance());
     }
 
     @Test
@@ -165,33 +166,33 @@
         Agent agent = new Agent(mIrs, mScribe);
         Ledger ledger = new Ledger();
 
-        doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
+        doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked();
         doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance();
 
-        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5);
+        Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(5, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 995);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1000, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -500);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(500, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 1000);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1_000, ledger.getCurrentBalance());
 
         // Shouldn't change in normal operation, but adding test case in case it does.
         doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance();
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, 500);
+        transaction = new Ledger.Transaction(0, 0, 0, null, 500, 0);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(1_000, ledger.getCurrentBalance());
 
-        transaction = new Ledger.Transaction(0, 0, 0, null, -1001);
+        transaction = new Ledger.Transaction(0, 0, 0, null, -1001, 500);
         agent.recordTransactionLocked(0, "com.test", ledger, transaction, false);
         assertEquals(-1, ledger.getCurrentBalance());
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index b72fc23..c2cf2ff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -107,20 +107,32 @@
     @Test
     public void testWriteHighLevelStateToDisk() {
         long lastReclamationTime = System.currentTimeMillis();
-        long narcsInCirculation = 2000L;
+        long remainingConsumableNarcs = 2000L;
+        long consumptionLimit = 500_000L;
+        when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit);
 
         Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000));
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000, 0));
         // Negative ledger balance shouldn't affect the total circulation value.
         ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE);
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000));
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
         mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
-        mScribeUnderTest.writeImmediatelyForTesting();
+        mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
+        mScribeUnderTest.adjustRemainingConsumableNarcsLocked(
+                remainingConsumableNarcs - consumptionLimit);
 
+        assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
+        assertEquals(remainingConsumableNarcs,
+                mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+
+        mScribeUnderTest.writeImmediatelyForTesting();
         mScribeUnderTest.loadFromDiskLocked();
 
         assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
-        assertEquals(narcsInCirculation, mScribeUnderTest.getNarcsInCirculationLocked());
+        assertEquals(remainingConsumableNarcs,
+                mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
     }
 
     @Test
@@ -135,9 +147,9 @@
     @Test
     public void testWritingPopulatedLedgerToDisk() {
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, -1));
+        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 12));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         mScribeUnderTest.loadFromDiskLocked();
@@ -156,11 +168,11 @@
                 addInstalledPackage(userId, pkgName);
                 final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName);
                 ledger.recordTransaction(new Ledger.Transaction(
-                        0, 1000L * u + l, 1, null, 51L * u + l));
+                        0, 1000L * u + l, 1, null, -51L * u + l, 50));
                 ledger.recordTransaction(new Ledger.Transaction(
-                        1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l));
+                        1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l, 0));
                 ledger.recordTransaction(new Ledger.Transaction(
-                        2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l));
+                        2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l, 0));
                 ledgers.add(userId, pkgName, ledger);
             }
         }
@@ -174,9 +186,9 @@
     @Test
     public void testDiscardLedgerFromDisk() {
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 0));
+        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 1));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         mScribeUnderTest.loadFromDiskLocked();
@@ -195,9 +207,9 @@
     public void testLoadingMissingPackageFromDisk() {
         final String pkgName = TEST_PACKAGE + ".uninstalled";
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
+        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 2));
+        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         // Package isn't installed, so make sure it's not saved to memory after loading.
@@ -209,9 +221,9 @@
     public void testLoadingMissingUserFromDisk() {
         final int userId = TEST_USER_ID + 1;
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
+        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
+        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 1));
+        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         // User doesn't show up with any packages, so make sure nothing is saved after loading.
@@ -219,6 +231,37 @@
         assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE));
     }
 
+    @Test
+    public void testChangingConsumable() {
+        assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(0, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+        // Limit increased, so remaining value should be adjusted as well
+        mScribeUnderTest.setConsumptionLimitLocked(1000);
+        assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(1000, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+        // Limit decreased below remaining, so remaining value should be adjusted as well
+        mScribeUnderTest.setConsumptionLimitLocked(500);
+        assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(500, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+        mScribeUnderTest.adjustRemainingConsumableNarcsLocked(-100);
+        assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(400, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+        // Limit increased, so remaining value should be adjusted by the difference as well
+        mScribeUnderTest.setConsumptionLimitLocked(1000);
+        assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+
+
+        // Limit decreased, but above remaining, so remaining value should left alone
+        mScribeUnderTest.setConsumptionLimitLocked(950);
+        assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
+        assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+    }
+
     private void assertLedgersEqual(Ledger expected, Ledger actual) {
         if (expected == null) {
             assertNull(actual);
@@ -245,6 +288,7 @@
         assertEquals(expected.eventId, actual.eventId);
         assertEquals(expected.tag, actual.tag);
         assertEquals(expected.delta, actual.delta);
+        assertEquals(expected.ctp, actual.ctp);
     }
 
     private void addInstalledPackage(int userId, String pkgName) {
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 68994e6..177d72b 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,5 +1,6 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
 per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index a9b7cfb..3ce2ed8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -52,7 +52,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
 import com.android.server.wm.WindowManagerInternal;
@@ -94,7 +93,6 @@
     final FullScreenMagnificationController.ControllerContext mMockControllerCtx =
             mock(FullScreenMagnificationController.ControllerContext.class);
     final Context mMockContext = mock(Context.class);
-    final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
     final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
     private final MagnificationAnimationCallback mAnimationCallback = mock(
@@ -121,12 +119,10 @@
         // Pretending ID of the Thread associated with looper as main thread ID in controller
         when(mMockContext.getMainLooper()).thenReturn(looper);
         when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
-        when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
         when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
         when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
         when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
         when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
-        when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
         initMockWindowManager();
 
         mFullScreenMagnificationController = new FullScreenMagnificationController(
@@ -357,8 +353,8 @@
         assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
         assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
         assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
-        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
-                mConfigCaptor.capture());
+        verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
+                eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
         assertConfigEquals(config, mConfigCaptor.getValue());
         verify(mMockValueAnimator).start();
         verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
@@ -501,7 +497,7 @@
         mMessageCapturingHandler.sendAllMessages();
         MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(),
                 OTHER_MAGNIFICATION_BOUNDS.centerY());
-        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION),
+        verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId), eq(OTHER_REGION),
                 mConfigCaptor.capture());
         assertConfigEquals(config, mConfigCaptor.getValue());
     }
@@ -655,9 +651,9 @@
         register(displayId);
         zoomIn2xToMiddle(displayId);
         mMessageCapturingHandler.sendAllMessages();
-        reset(mMockAms);
+        reset(mRequestObserver);
         assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
-        verify(mMockAms).notifyMagnificationChanged(eq(displayId),
+        verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
                 eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
         assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
         assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
@@ -676,8 +672,8 @@
         assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
         mMessageCapturingHandler.sendAllMessages();
 
-        verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class),
-                any(MagnificationConfig.class));
+        verify(mRequestObserver, never()).onFullScreenMagnificationChanged(eq(displayId),
+                any(Region.class), any(MagnificationConfig.class));
         verify(mAnimationCallback).onResult(true);
     }
 
@@ -1072,8 +1068,8 @@
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
         verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
-        verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
-                mConfigCaptor.capture());
+        verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
+                eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
         assertConfigEquals(config, mConfigCaptor.getValue());
         Mockito.reset(mMockWindowManager);
 
@@ -1097,7 +1093,7 @@
 
         // Animation should have been restarted
         verify(mMockValueAnimator, times(2)).start();
-        verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId),
+        verify(mRequestObserver, times(2)).onFullScreenMagnificationChanged(eq(displayId),
                 eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
         assertConfigEquals(newConfig, mConfigCaptor.getValue());
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 2060223..0fed89b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -151,8 +151,6 @@
                 mock(FullScreenMagnificationController.ControllerContext.class);
         final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
         when(mockController.getContext()).thenReturn(mContext);
-        when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
-        when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
         when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
         when(mockController.getWindowManager()).thenReturn(mockWindowManager);
         when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 3fcce92..ec59090 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -145,9 +145,10 @@
                         mCallbackDelegate, mTraceManager, mScaleProvider));
         mMockConnection = new MockWindowMagnificationConnection(true);
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        new FullScreenMagnificationControllerStubber(mScreenMagnificationController);
         mMagnificationController = new MagnificationController(mService, globalLock, mContext,
                 mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider);
+        new FullScreenMagnificationControllerStubber(mScreenMagnificationController,
+                mMagnificationController);
 
         mMagnificationController.setMagnificationCapabilities(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -451,6 +452,29 @@
     }
 
     @Test
+    public void onFullScreenMagnificationChanged_fullScreenEnabled_notifyMagnificationChanged()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_FULLSCREEN);
+
+        final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN);
+        mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
+                config.getScale(), config.getCenterX(), config.getCenterY(),
+                true, TEST_SERVICE_ID);
+
+        // The first time is triggered when setting magnification enabled. And the second time is
+        // triggered when calling setScaleAndCenter.
+        final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
+                MagnificationConfig.class);
+        verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY),
+                eq(FullScreenMagnificationControllerStubber.MAGNIFICATION_REGION),
+                configCaptor.capture());
+        final MagnificationConfig actualConfig = configCaptor.getValue();
+        assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0);
+        assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
+        assertEquals(config.getScale(), actualConfig.getScale(), 0);
+    }
+
+    @Test
     public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
             throws RemoteException {
         setMagnificationEnabled(MODE_WINDOW);
@@ -679,7 +703,7 @@
             throws RemoteException {
         setMagnificationEnabled(MODE_FULLSCREEN);
         mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
-                /* scale= */1, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+                /* scale= */ 1, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
                 true, TEST_SERVICE_ID);
 
         mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
@@ -884,6 +908,8 @@
     private static class FullScreenMagnificationControllerStubber {
         private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
         private final FullScreenMagnificationController mScreenMagnificationController;
+        private final FullScreenMagnificationController.MagnificationInfoChangedCallback
+                mMagnificationChangedCallback;
         private boolean mIsMagnifying = false;
         private float mScale = 1.0f;
         private float mCenterX = MAGNIFICATION_REGION.getBounds().exactCenterX();
@@ -891,8 +917,10 @@
         private int mServiceId = -1;
 
         FullScreenMagnificationControllerStubber(
-                FullScreenMagnificationController screenMagnificationController) {
+                FullScreenMagnificationController screenMagnificationController,
+                FullScreenMagnificationController.MagnificationInfoChangedCallback callback) {
             mScreenMagnificationController = screenMagnificationController;
+            mMagnificationChangedCallback = callback;
             stubMethods();
         }
 
@@ -930,6 +958,14 @@
                 } else {
                     reset();
                 }
+
+
+                final MagnificationConfig config = new MagnificationConfig.Builder().setMode(
+                        MODE_FULLSCREEN).setScale(mScale).setCenterX(mCenterX).setCenterY(
+                        mCenterY).build();
+                mMagnificationChangedCallback.onFullScreenMagnificationChanged(TEST_DISPLAY,
+                        FullScreenMagnificationControllerStubber.MAGNIFICATION_REGION,
+                        config);
                 return true;
             };
             doAnswer(setScaleAndCenterStubAnswer).when(
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index 877538c..782d519 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -20,7 +20,12 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
@@ -42,6 +47,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -51,6 +57,7 @@
 @SmallTest
 @Presubmit
 public class AnrHelperTest {
+    private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
     private AnrHelper mAnrHelper;
 
     private ProcessRecord mAnrApp;
@@ -106,8 +113,42 @@
         mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
                 parentShortComponentName, parentProcess, aboveSystem, annotation);
 
-        verify(mAnrApp.mErrorState, timeout(TimeUnit.SECONDS.toMillis(5))).appNotResponding(
+        verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
                 eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
                 eq(parentProcess), eq(aboveSystem), eq(annotation), eq(false) /* onlyDumpSelf */);
     }
+
+    @Test
+    public void testSkipDuplicatedAnr() {
+        final CountDownLatch consumerLatch = new CountDownLatch(1);
+        final CountDownLatch processingLatch = new CountDownLatch(1);
+        doAnswer(invocation -> {
+            consumerLatch.countDown();
+            // Simulate that it is dumping to block the consumer thread.
+            processingLatch.await();
+            return null;
+        }).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
+                anyBoolean(), anyString(), anyBoolean());
+        final ApplicationInfo appInfo = new ApplicationInfo();
+        mAnrApp.mPid = 12345;
+        final Runnable reportAnr = () -> mAnrHelper.appNotResponding(mAnrApp,
+                "activityShortComponentName", appInfo, "parentShortComponentName",
+                null /* parentProcess */, false /* aboveSystem */, "annotation");
+        reportAnr.run();
+        // This should be skipped because the pid is pending in queue.
+        reportAnr.run();
+        // The first reported ANR must be processed.
+        try {
+            assertTrue(consumerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException ignored) {
+        }
+        // This should be skipped because the pid is under processing.
+        reportAnr.run();
+
+        // Assume that the first one finishes after all incoming ANRs.
+        processingLatch.countDown();
+        // There is only one ANR reported.
+        verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
+                anyString(), any(), any(), any(), anyBoolean(), anyString(), anyBoolean());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3890d4d..897b91e 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -37,7 +37,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
-import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
@@ -48,7 +48,7 @@
 import android.provider.DeviceConfig;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
-import android.service.attention.IProximityCallback;
+import android.service.attention.IProximityUpdateCallback;
 
 import androidx.test.filters.SmallTest;
 
@@ -85,7 +85,7 @@
     @Mock
     Context mContext;
     @Mock
-    private ProximityCallbackInternal mMockProximityCallbackInternal;
+    private ProximityUpdateCallbackInternal mMockProximityUpdateCallbackInternal;
 
     @Before
     public void setUp() throws RemoteException {
@@ -119,7 +119,8 @@
     public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
         mSpyAttentionManager.mIsServiceEnabled = false;
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -128,7 +129,8 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -139,7 +141,8 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(false).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isFalse();
     }
 
@@ -149,9 +152,10 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
-        verify(mMockProximityCallbackInternal, times(1))
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
     }
 
@@ -161,21 +165,23 @@
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
 
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
 
         ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
-        assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+        assertThat(mSpyAttentionManager.onStartProximityUpdates(
+                mMockProximityUpdateCallbackInternal))
                 .isTrue();
         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
-        verify(mMockProximityCallbackInternal, times(1))
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
     }
 
     @Test
     public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        verifyZeroInteractions(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -184,11 +190,11 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        verify(mMockProximityCallbackInternal, times(1))
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
 
-        ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() {
+        ProximityUpdateCallbackInternal mismatchedCallback = new ProximityUpdateCallbackInternal() {
             @Override
             public void onProximityUpdate(double distance) {
                 fail("Callback shouldn't have responded.");
@@ -196,7 +202,7 @@
         };
         mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
 
-        verifyNoMoreInteractions(mMockProximityCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -205,8 +211,8 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
 
         assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
     }
@@ -217,14 +223,14 @@
         mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
-        verify(mMockProximityCallbackInternal, times(1))
+        mSpyAttentionManager.onStartProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verify(mMockProximityUpdateCallbackInternal, times(1))
                 .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
 
         // Attention Service unregisters the proximity update twice in a row.
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
-        verifyNoMoreInteractions(mMockProximityCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
@@ -337,7 +343,8 @@
         public void cancelAttentionCheck(IAttentionCallback callback) {
         }
 
-        public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException {
+        public void onStartProximityUpdates(IProximityUpdateCallback callback)
+                throws RemoteException {
             callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
new file mode 100644
index 0000000..b154d6f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.backup.transport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.backup.ITransportStatusCallback;
+import com.android.internal.infra.AndroidFuture;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupTransportClientTest {
+
+    private static class TestFuturesFakeTransportBinder extends FakeTransportBinderBase {
+        public final Object mLock = new Object();
+
+        public String mNameCompletedImmediately;
+
+        @GuardedBy("mLock")
+        public AndroidFuture<String> mNameCompletedInFuture;
+
+        @Override public void name(AndroidFuture<String> name) throws RemoteException {
+            name.complete(mNameCompletedImmediately);
+        }
+        @Override public void transportDirName(AndroidFuture<String> name) throws RemoteException {
+            synchronized (mLock) {
+                mNameCompletedInFuture = name;
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    @Test
+    public void testName_completesImmediately_returnsName() throws Exception {
+        TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+        binder.mNameCompletedImmediately = "fake name";
+
+        BackupTransportClient client = new BackupTransportClient(binder);
+        String name = client.name();
+
+        assertThat(name).isEqualTo("fake name");
+    }
+
+    @Test
+    public void testTransportDirName_completesLater_returnsName() throws Exception {
+        TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+        BackupTransportClient client = new BackupTransportClient(binder);
+
+        Thread thread = new Thread(() -> {
+            try {
+                String name = client.transportDirName();
+                assertThat(name).isEqualTo("fake name");
+            } catch (Exception ex) {
+                fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+            }
+        });
+
+        thread.start();
+
+        synchronized (binder.mLock) {
+            while (binder.mNameCompletedInFuture == null) {
+                binder.mLock.wait();
+            }
+            assertThat(binder.mNameCompletedInFuture.complete("fake name")).isTrue();
+        }
+
+        thread.join();
+    }
+
+    @Test
+    public void testTransportDirName_canceledBeforeCompletion_throwsException() throws Exception {
+        TestFuturesFakeTransportBinder binder = new TestFuturesFakeTransportBinder();
+        BackupTransportClient client = new BackupTransportClient(binder);
+
+        Thread thread = new Thread(() -> {
+            try {
+                /*String name =*/ client.transportDirName();
+                fail("transportDirName should be cancelled");
+            } catch (CancellationException ex) {
+                // This is expected.
+            } catch (Exception ex) {
+                fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+            }
+        });
+
+        thread.start();
+
+        synchronized (binder.mLock) {
+            while (binder.mNameCompletedInFuture == null) {
+                binder.mLock.wait();
+            }
+            client.onBecomingUnusable();
+        }
+
+        thread.join();
+    }
+
+    private static class TestCallbacksFakeTransportBinder extends FakeTransportBinderBase {
+        public final Object mLock = new Object();
+
+        public int mStatusCompletedImmediately;
+
+        @GuardedBy("mLock")
+        public ITransportStatusCallback mStatusCompletedInFuture;
+
+        @Override public void initializeDevice(ITransportStatusCallback c) throws RemoteException {
+            c.onOperationCompleteWithStatus(mStatusCompletedImmediately);
+        }
+        @Override public void finishBackup(ITransportStatusCallback c) throws RemoteException {
+            synchronized (mLock) {
+                mStatusCompletedInFuture = c;
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    @Test
+    public void testInitializeDevice_completesImmediately_returnsStatus() throws Exception {
+        TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+        binder.mStatusCompletedImmediately = 123;
+
+        BackupTransportClient client = new BackupTransportClient(binder);
+        int status = client.initializeDevice();
+
+        assertThat(status).isEqualTo(123);
+    }
+
+
+    @Test
+    public void testFinishBackup_completesLater_returnsStatus() throws Exception {
+        TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+        BackupTransportClient client = new BackupTransportClient(binder);
+
+        Thread thread = new Thread(() -> {
+            try {
+                int status = client.finishBackup();
+                assertThat(status).isEqualTo(456);
+            } catch (Exception ex) {
+                fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+            }
+        });
+
+        thread.start();
+
+        synchronized (binder.mLock) {
+            while (binder.mStatusCompletedInFuture == null) {
+                binder.mLock.wait();
+            }
+            binder.mStatusCompletedInFuture.onOperationCompleteWithStatus(456);
+        }
+
+        thread.join();
+    }
+
+    @Test
+    public void testFinishBackup_canceledBeforeCompletion_throwsException() throws Exception {
+        TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
+        BackupTransportClient client = new BackupTransportClient(binder);
+
+        Thread thread = new Thread(() -> {
+            try {
+                int status = client.finishBackup();
+                assertThat(status).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+            } catch (Exception ex) {
+                fail("unexpected Exception: " + ex.getClass().getCanonicalName());
+            }
+        });
+
+        thread.start();
+
+        synchronized (binder.mLock) {
+            while (binder.mStatusCompletedInFuture == null) {
+                binder.mLock.wait();
+            }
+            client.onBecomingUnusable();
+        }
+
+        thread.join();
+    }
+
+    // Convenience layer so we only need to fake specific methods useful for each test case.
+    private static class FakeTransportBinderBase implements IBackupTransport {
+        @Override public void name(AndroidFuture<String> f) throws RemoteException {}
+        @Override public void transportDirName(AndroidFuture<String> f) throws RemoteException {}
+        @Override public void configurationIntent(AndroidFuture<Intent> f) throws RemoteException {}
+        @Override public void currentDestinationString(AndroidFuture<String> f)
+                throws RemoteException {}
+        @Override public void dataManagementIntent(AndroidFuture<Intent> f)
+                throws RemoteException {}
+        @Override public void dataManagementIntentLabel(AndroidFuture<CharSequence> f)
+                throws RemoteException {}
+        @Override public void initializeDevice(ITransportStatusCallback c) throws RemoteException {}
+        @Override public void clearBackupData(PackageInfo i, ITransportStatusCallback c)
+                throws RemoteException {}
+        @Override public void finishBackup(ITransportStatusCallback c) throws RemoteException {}
+        @Override public void requestBackupTime(AndroidFuture<Long> f) throws RemoteException {}
+        @Override public void performBackup(PackageInfo i, ParcelFileDescriptor fd, int f,
+            ITransportStatusCallback c) throws RemoteException {}
+        @Override public void getAvailableRestoreSets(AndroidFuture<List<RestoreSet>> f)
+                throws RemoteException {}
+        @Override public void getCurrentRestoreSet(AndroidFuture<Long> f) throws RemoteException {}
+        @Override public void startRestore(long t, PackageInfo[] p, ITransportStatusCallback c)
+                throws RemoteException {}
+        @Override public void nextRestorePackage(AndroidFuture<RestoreDescription> f)
+                throws RemoteException {}
+        @Override public void getRestoreData(ParcelFileDescriptor fd, ITransportStatusCallback c)
+                throws RemoteException {}
+        @Override public void finishRestore(ITransportStatusCallback c) throws RemoteException {}
+        @Override public void requestFullBackupTime(AndroidFuture<Long> f) throws RemoteException {}
+        @Override public void performFullBackup(PackageInfo i, ParcelFileDescriptor fd, int f,
+            ITransportStatusCallback c) throws RemoteException {}
+        @Override public void checkFullBackupSize(long s, ITransportStatusCallback c)
+                throws RemoteException {}
+        @Override public void sendBackupData(int n, ITransportStatusCallback c)
+                throws RemoteException {}
+        @Override public void cancelFullBackup(ITransportStatusCallback c) throws RemoteException {}
+        @Override public void isAppEligibleForBackup(PackageInfo p, boolean b,
+            AndroidFuture<Boolean> f) throws RemoteException {}
+        @Override public void getBackupQuota(String s, boolean b, AndroidFuture<Long> f)
+                throws RemoteException {}
+        @Override public void getNextFullRestoreDataChunk(ParcelFileDescriptor fd,
+            ITransportStatusCallback c) throws RemoteException {}
+        @Override public void abortFullRestore(ITransportStatusCallback c) throws RemoteException {}
+        @Override public void getTransportFlags(AndroidFuture<Integer> f) throws RemoteException {}
+        @Override public IBinder asBinder() {
+            return null;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2ae2854..9aac81c 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -361,6 +361,16 @@
     }
 
     @Test
+    public void close_cleanVirtualAudioController() {
+        mDeviceImpl.onVirtualDisplayCreatedLocked(DISPLAY_ID);
+        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mCallback);
+
+        mDeviceImpl.close();
+
+        assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull();
+    }
+
+    @Test
     public void sendKeyEvent_noFd() {
         assertThrows(
                 IllegalArgumentException.class,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index fe110e5..18c6931 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1137,9 +1137,6 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
         // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         verify(getServices().userManager, times(1)).setUserRestriction(
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(true), eq(UserHandle.SYSTEM));
@@ -1205,9 +1202,6 @@
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         // Verify internal calls.
-        verify(getServices().iactivityManager).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1392,11 +1386,6 @@
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
         assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
-        // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
@@ -1501,11 +1490,6 @@
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
         assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
-        // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         // Now call clear from the secondary user, which should throw.
@@ -3575,11 +3559,11 @@
         setup_DeviceAdminFeatureOff();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+                DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
-                DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+                DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED);
+                DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED);
     }
 
     private void setup_ManagedProfileFeatureOff() throws Exception {
@@ -3611,11 +3595,11 @@
         setup_ManagedProfileFeatureOff();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED);
+                DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED);
     }
 
     private void setup_firstBoot_systemUser() throws Exception {
@@ -3653,15 +3637,15 @@
 
         when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(false);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
 
         when(getServices().userManagerForMock.isHeadlessSystemUserMode()).thenReturn(true);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
     }
 
     private void setup_systemUserSetupComplete_systemUser() throws Exception {
@@ -3708,11 +3692,11 @@
         setup_systemUserSetupComplete_systemUser();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
+                DevicePolicyManager.STATUS_USER_SETUP_COMPLETED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
-                DevicePolicyManager.CODE_USER_SETUP_COMPLETED);
+                DevicePolicyManager.STATUS_USER_SETUP_COMPLETED);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_OK);
+                DevicePolicyManager.STATUS_OK);
     }
 
     @Test
@@ -3722,22 +3706,22 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
-                DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+                DevicePolicyManager.STATUS_HAS_DEVICE_OWNER);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE,
-                DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+                DevicePolicyManager.STATUS_HAS_DEVICE_OWNER);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, false);
 
         // COMP mode NOT is allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
 
         // And other DPCs can NOT provision a managed profile.
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DpmMockContext.ANOTHER_PACKAGE_NAME,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
                 DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
     }
@@ -3759,13 +3743,13 @@
                 eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
                 .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
 
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DpmMockContext.ANOTHER_PACKAGE_NAME,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
                 DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
     }
@@ -3794,12 +3778,12 @@
 
         // We can delete the managed profile to create a new one, so provisioning is allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DpmMockContext.ANOTHER_PACKAGE_NAME,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
                 DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
     }
@@ -3823,13 +3807,13 @@
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DpmMockContext.ANOTHER_PACKAGE_NAME,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
                 DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
 
         // But the device owner can still do it because it has set the restriction itself.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
         assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
     }
 
@@ -3901,7 +3885,7 @@
 
         // COMP mode is NOT allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
     }
 
     private void setup_provisionManagedProfileOneAlreadyExist_primaryUser() throws Exception {
@@ -3935,14 +3919,14 @@
         setup_provisionManagedProfileOneAlreadyExist_primaryUser();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.STATUS_CANNOT_ADD_MANAGED_PROFILE);
     }
 
     @Test
     public void testCheckProvisioningPreCondition_permission() {
         // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
         assertExpectException(SecurityException.class, /* messageRegex =*/ null,
-                () -> dpm.checkProvisioningPreCondition(
+                () -> dpm.checkProvisioningPrecondition(
                         DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package"));
     }
 
@@ -8475,7 +8459,7 @@
 
     @Test
     public void testSendLostModeLocationUpdate_notOrganizationOwnedDevice() {
-        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
         assertThrows(IllegalStateException.class, () -> dpm.sendLostModeLocationUpdate(
                 getServices().executor, /* empty callback */ result -> {}));
     }
@@ -8483,7 +8467,7 @@
     @Test
     public void testSendLostModeLocationUpdate_asDeviceOwner() throws Exception {
         final String TEST_PROVIDER = "network";
-        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
         setDeviceOwner();
         when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
         when(getServices().locationManager.isProviderEnabled(TEST_PROVIDER)).thenReturn(true);
@@ -8500,7 +8484,7 @@
         final int MANAGED_PROFILE_ADMIN_UID =
                 UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
-        mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+        mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
         addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
         configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
         when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
@@ -8696,7 +8680,7 @@
     private void assertCheckProvisioningPreCondition(
             String action, String packageName, int provisioningCondition) {
         assertWithMessage("checkProvisioningPreCondition(%s, %s) returning unexpected result",
-                action, packageName).that(dpm.checkProvisioningPreCondition(action, packageName))
+                action, packageName).that(dpm.checkProvisioningPrecondition(action, packageName))
                         .isEqualTo(provisioningCondition);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index bdf94f3..356600d 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -33,6 +33,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
 import android.database.ContentObserver;
+import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.display.AmbientBrightnessDayStats;
@@ -42,6 +43,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.input.InputSensorInfo;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -63,6 +65,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -84,8 +88,11 @@
     private static final String DEFAULT_DISPLAY_ID = "123";
     private static final float FLOAT_DELTA = 0.01f;
 
+    @Mock private InputSensorInfo mInputSensorInfoMock;
+
     private BrightnessTracker mTracker;
     private TestInjector mInjector;
+    private Sensor mLightSensorFake;
 
     private static Object sHandlerLock = new Object();
     private static Handler sHandler;
@@ -108,9 +115,12 @@
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mInjector = new TestInjector(ensureHandler());
+        mLightSensorFake = new Sensor(mInputSensorInfoMock);
 
         mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector);
+        mTracker.setLightSensor(mLightSensorFake);
         mDefaultNightModeColorTemperature =
                 InstrumentationRegistry.getContext().getResources().getInteger(
                 R.integer.config_nightDisplayColorTemperatureDefault);
@@ -834,6 +844,47 @@
         mTracker.stop();
     }
 
+    @Test
+    public void testLightSensorChange() {
+        // verify the tracker started correctly and a listener registered
+        startTracker(mTracker);
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+        // Setting the sensor to null should stop the registered listener.
+        mTracker.setLightSensor(null);
+        mInjector.waitForHandler();
+        assertNull(mInjector.mSensorListener);
+        assertNull(mInjector.mLightSensor);
+
+        // Resetting sensor should start listener again
+        mTracker.setLightSensor(mLightSensorFake);
+        mInjector.waitForHandler();
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+        Sensor secondSensor = new Sensor(mInputSensorInfoMock);
+        // Setting a different listener should keep things working
+        mTracker.setLightSensor(secondSensor);
+        mInjector.waitForHandler();
+        assertNotNull(mInjector.mSensorListener);
+        assertEquals(mInjector.mLightSensor, secondSensor);
+    }
+
+    @Test
+    public void testSetLightSensorDoesntStartListener() {
+        mTracker.setLightSensor(mLightSensorFake);
+        assertNull(mInjector.mSensorListener);
+    }
+
+    @Test
+    public void testNullLightSensorWontRegister() {
+        mTracker.setLightSensor(null);
+        startTracker(mTracker);
+        assertNull(mInjector.mSensorListener);
+        assertNull(mInjector.mLightSensor);
+    }
+
     private InputStream getInputStream(String data) {
         return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
     }
@@ -924,6 +975,7 @@
 
     private class TestInjector extends BrightnessTracker.Injector {
         SensorEventListener mSensorListener;
+        Sensor mLightSensor;
         BroadcastReceiver mBroadcastReceiver;
         DisplayManager.DisplayListener mDisplayListener;
         Map<String, Integer> mSecureIntSettings = new HashMap<>();
@@ -974,14 +1026,16 @@
 
         @Override
         public void registerSensorListener(Context context,
-                SensorEventListener sensorListener, Handler handler) {
+                SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
             mSensorListener = sensorListener;
+            mLightSensor = lightSensor;
         }
 
         @Override
         public void unregisterSensorListener(Context context,
                 SensorEventListener sensorListener) {
             mSensorListener = null;
+            mLightSensor = null;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index fefc425..86b6da0 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -28,7 +28,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.verify;
@@ -70,6 +72,8 @@
 @RunWith(AndroidJUnit4.class)
 public class LogicalDisplayMapperTest {
     private static int sUniqueTestDisplayId = 0;
+    private static final int DEVICE_STATE_CLOSED = 0;
+    private static final int DEVICE_STATE_OPEN = 2;
 
     private DisplayDeviceRepository mDisplayDeviceRepo;
     private LogicalDisplayMapper mLogicalDisplayMapper;
@@ -113,6 +117,7 @@
 
         mPowerManager = new PowerManager(mContextMock, mIPowerManagerMock, mIThermalServiceMock,
                 null);
+
         when(mContextMock.getSystemServiceName(PowerManager.class))
                 .thenReturn(Context.POWER_SERVICE);
         when(mContextMock.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
@@ -120,6 +125,12 @@
         when(mResourcesMock.getBoolean(
                 com.android.internal.R.bool.config_supportsConcurrentInternalDisplays))
                 .thenReturn(true);
+        when(mResourcesMock.getIntArray(
+                com.android.internal.R.array.config_deviceStatesOnWhichToWakeUp))
+                .thenReturn(new int[]{1, 2});
+        when(mResourcesMock.getIntArray(
+                com.android.internal.R.array.config_deviceStatesOnWhichToSleep))
+                .thenReturn(new int[]{0});
 
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
@@ -312,6 +323,49 @@
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
     }
 
+    @Test
+    public void testDeviceShouldBeWoken() {
+        assertTrue(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_OPEN,
+                DEVICE_STATE_CLOSED,
+                /* isInteractive= */false,
+                /* isBootCompleted= */true));
+    }
+
+    @Test
+    public void testDeviceShouldNotBeWoken() {
+        assertFalse(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_CLOSED,
+                DEVICE_STATE_OPEN,
+                /* isInteractive= */false,
+                /* isBootCompleted= */true));
+    }
+
+    @Test
+    public void testDeviceShouldBePutToSleep() {
+        assertTrue(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
+                DEVICE_STATE_OPEN,
+                /* isOverrideActive= */false,
+                /* isInteractive= */true,
+                /* isBootCompleted= */true));
+    }
+
+    @Test
+    public void testDeviceShouldNotBePutToSleep() {
+        assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_OPEN,
+                DEVICE_STATE_CLOSED,
+                /* isOverrideActive= */false,
+                /* isInteractive= */true,
+                /* isBootCompleted= */true));
+    }
+
+    @Test
+    public void testDeviceShouldNotBePutToSleepDifferentBaseState() {
+        assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
+                DEVICE_STATE_OPEN,
+                /* isOverrideActive= */true,
+                /* isInteractive= */true,
+                /* isBootCompleted= */true));
+    }
+
     /////////////////
     // Helper Methods
     /////////////////
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 2f5993d1..5d3da43 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -132,7 +132,12 @@
 
     @Test
     public void testGetApexSystemServices() throws RemoteException {
-        when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, false));
+        when(mApexService.getAllPackages()).thenReturn(new ApexInfo[] {
+                createApexInfoForTestPkg(false, true, 1),
+                // only active apex reports apex-system-service
+                createApexInfoForTestPkg(true, false, 2),
+        });
+
         mApexManager.scanApexPackagesTraced(mPackageParser2,
                 ParallelPackageParser.makeExecutorService());
 
@@ -484,17 +489,20 @@
         assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
     }
 
-    private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+    private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) {
         File apexFile = extractResource(TEST_APEX_PKG,  TEST_APEX_FILE_NAME);
         ApexInfo apexInfo = new ApexInfo();
         apexInfo.isActive = isActive;
         apexInfo.isFactory = isFactory;
         apexInfo.moduleName = TEST_APEX_PKG;
         apexInfo.modulePath = apexFile.getPath();
-        apexInfo.versionCode = 191000070;
+        apexInfo.versionCode = version;
         apexInfo.preinstalledModulePath = apexFile.getPath();
+        return apexInfo;
+    }
 
-        return new ApexInfo[]{apexInfo};
+    private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+        return new ApexInfo[]{createApexInfoForTestPkg(isActive, isFactory, 191000070)};
     }
 
     private ApexInfo createApexInfo(String moduleName, int versionCode, boolean isActive,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 3d21b74..25ca1e2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -24,6 +24,7 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
 import android.platform.test.annotations.Presubmit;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -188,7 +189,7 @@
                 /* isFailed */ false,
                 /* isApplied */false,
                 /* stagedSessionErrorCode */
-                PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED,
+                PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                 /* stagedSessionErrorMessage */ "some error");
     }
 
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 8873f42..2b34bc2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -35,6 +35,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -109,6 +110,8 @@
     LegacyPermissionDataProvider mPermissionDataProvider;
     @Mock
     DomainVerificationManagerInternal mDomainVerificationManager;
+    @Mock
+    Computer computer;
 
     final ArrayMap<String, Long> mOrigFirstInstallTimes = new ArrayMap<>();
 
@@ -132,7 +135,7 @@
         /* write out files and read */
         writeOldFiles();
         Settings settings = makeSettings();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
         verifyKeySetMetaData(settings);
     }
 
@@ -143,11 +146,11 @@
         // write out files and read
         writeOldFiles();
         Settings settings = makeSettings();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
 
         // write out, read back in and verify the same
-        settings.writeLPr();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        settings.writeLPr(computer);
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
         verifyKeySetMetaData(settings);
     }
 
@@ -156,7 +159,7 @@
         // Write delegateshellthe package files and make sure they're parsed properly the first time
         writeOldFiles();
         Settings settings = makeSettings();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
 
@@ -175,12 +178,12 @@
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
         Settings settings = makeSettings();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
-        settings.writeLPr();
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+        settings.writeLPr(computer);
 
         // Create Settings again to make it read from the new files
         settings = makeSettings();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
 
         PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
         assertThat(ps.getEnabled(0), is(COMPONENT_ENABLED_STATE_DISABLED_USER));
@@ -225,7 +228,11 @@
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
         assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
-        assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+                is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+                is(nullValue()));
 
         // Verify that the snapshot returns the same answers
         ps1 = snapshot.mPackages.get(PACKAGE_NAME_1);
@@ -233,7 +240,11 @@
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
         assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
-        assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+                is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+                is(nullValue()));
 
         PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
         PackageUserStateInternal packageUserState2 = ps2.readUserState(0);
@@ -315,14 +326,14 @@
                 .build();
 
         ps1.modifyUserState(0).putSuspendParams( "suspendingPackage1",
-                SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+                new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
         ps1.modifyUserState(0).putSuspendParams( "suspendingPackage2",
-                SuspendParams.getInstanceOrNull(dialogInfo2, appExtras2, launcherExtras2));
+                new SuspendParams(dialogInfo2, appExtras2, launcherExtras2));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
         watcher.verifyChangeReported("put package 1");
 
         ps2.modifyUserState(0).putSuspendParams( "suspendingPackage3",
-                SuspendParams.getInstanceOrNull(null, appExtras1, null));
+                new SuspendParams(null, appExtras1, null));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
         watcher.verifyChangeReported("put package 2");
 
@@ -461,12 +472,12 @@
         ps2.setUsesStaticLibrariesVersions(new long[] { 34 });
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
 
-        settingsUnderTest.writeLPr();
+        settingsUnderTest.writeLPr(computer);
 
         settingsUnderTest.mPackages.clear();
         settingsUnderTest.mDisabledSysPackages.clear();
 
-        assertThat(settingsUnderTest.readLPw(createFakeUsers()), is(true));
+        assertThat(settingsUnderTest.readLPw(computer, createFakeUsers()), is(true));
 
         PackageSetting readPs1 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_1);
         PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
@@ -526,12 +537,12 @@
         ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 });
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
 
-        settingsUnderTest.writeLPr();
+        settingsUnderTest.writeLPr(computer);
 
         settingsUnderTest.mPackages.clear();
         settingsUnderTest.mDisabledSysPackages.clear();
 
-        assertThat(settingsUnderTest.readLPw(createFakeUsers()), is(true));
+        assertThat(settingsUnderTest.readLPw(computer, createFakeUsers()), is(true));
 
         PackageSetting readPs1 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_1);
         PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
@@ -579,7 +590,7 @@
         Settings settings = makeSettings();
         final WatchableTester watcher = new WatchableTester(settings, "testEnableDisable");
         watcher.register();
-        assertThat(settings.readLPw(createFakeUsers()), is(true));
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
         watcher.verifyChangeReported("readLPw");
 
         // Enable/Disable a package
@@ -686,7 +697,7 @@
                 .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
                 .build();
         origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
-                SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+                new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
         final PackageSetting testPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
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 4dc9612..9ad503c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -85,7 +85,7 @@
 
         oldUserState = new PackageUserStateImpl();
         oldUserState.putSuspendParams("suspendingPackage",
-                SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
+                new SuspendParams(null, new PersistableBundle(), null));
         assertThat(testUserState.equals(oldUserState), is(false));
 
         oldUserState = new PackageUserStateImpl();
@@ -185,7 +185,7 @@
 
     private static SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
             PersistableBundle appExtras, PersistableBundle launcherExtras) {
-        return SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+        return new SuspendParams(dialogInfo, appExtras, launcherExtras);
     }
 
     private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
diff --git a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
index 95af1e1..6e03569 100644
--- a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
@@ -145,7 +145,7 @@
         IntentFilter i = new IntentFilter("TEST_ACTION");
         PreferredActivity a1 = new PreferredActivity(i, 1, components, component, true);
 
-        r.addFilter(a1);
+        r.addFilter(null, a1);
         watcher.verifyChangeReported("addFilter");
         i.setPriority(i.getPriority() + 1);
         watcher.verifyNoChangeReported("indepenent intent");
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 c94168c..1b92017 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -321,7 +321,7 @@
     }
 
     private void startSystem() {
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         // Grab the BatteryReceiver
         ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -439,7 +439,7 @@
     @Test
     public void testGetDesiredScreenPolicy_WithVR() {
         createService();
-        mService.systemReady();
+        startSystem();
         // Brighten up the screen
         mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
                 null, null);
@@ -627,8 +627,7 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
         assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -638,8 +637,7 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
     }
@@ -647,8 +645,7 @@
     @Test
     public void testForceSuspend_putsDeviceToSleep() {
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -693,8 +690,7 @@
         //
         // TEST STARTS HERE
         //
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -792,7 +788,7 @@
         createService();
         assertTrue(isAcquired[0]);
 
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         assertTrue(isAcquired[0]);
 
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -1231,7 +1227,7 @@
     public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1444,7 +1440,7 @@
     @Test
     public void testSetPowerBoost_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
 
@@ -1454,7 +1450,7 @@
     @Test
     public void testSetPowerMode_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Enabled launch boost in BatterySaverController to allow setting launch mode.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1470,7 +1466,7 @@
     @Test
     public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1486,7 +1482,7 @@
     @Test
     public void testSetPowerModeChecked_returnsNativeCallResult() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1649,7 +1645,7 @@
     @Test
     public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
                 .thenReturn(mockReturnConfig);
@@ -1664,7 +1660,7 @@
     @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockSetPolicyConfig =
                 new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
@@ -1678,7 +1674,7 @@
     @Test
     public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1689,7 +1685,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1701,7 +1697,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1714,7 +1710,7 @@
     @Test
     public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
         mService.setDeviceIdleModeInternal(true);
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 16cfd13..6bdd88c 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@
 import android.os.IHwBinder;
 import android.os.IHwInterface;
 import android.os.RemoteException;
-import android.system.OsConstants;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,6 +58,7 @@
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(Parameterized.class)
 public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -259,10 +259,9 @@
         assertEquals(handle,
                 mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
 
-        verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
 
-        TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validateGenericSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
@@ -355,14 +354,16 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
-                modelCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
+
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -374,10 +375,10 @@
         assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
                 canonicalCallback));
 
-        verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
+                any());
 
-        TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validatePhraseSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index e687a2a..30b4a59 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -47,6 +47,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SharedMemory;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
 
@@ -82,6 +83,19 @@
                 HidlMemoryUtil.hidlMemoryToByteArray(model.data));
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
+        dup.header = model.header;
+        try {
+            dup.data = model.data.dup();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return dup;
+    }
+
     private static void validateSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
         assertEquals(type, model.type);
@@ -121,6 +135,15 @@
         validatePhrases_2_0(model.phrases);
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
+        dup.common = dupModel_2_1(model.common);
+        dup.phrases = model.phrases;
+        return dup;
+    }
+
     static void validatePhraseSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
         validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@
         properties.maxKeyPhrases = 567;
         properties.maxUsers = 678;
         properties.recognitionModes =
-                RecognitionMode.VOICE_TRIGGER
-                        | RecognitionMode.USER_IDENTIFICATION
-                        | RecognitionMode.USER_AUTHENTICATION
-                        | RecognitionMode.GENERIC_TRIGGER;
+                RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
+                        | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
         properties.captureTransition = true;
         properties.maxBufferMs = 321;
         properties.concurrentCapture = supportConcurrentCapture;
         properties.triggerInEvent = true;
         properties.powerConsumptionMw = 432;
         properties.supportedModelArch = "supportedModelArch";
-        properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        properties.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return properties;
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
         validateDefaultProperties(properties, supportConcurrentCapture,
                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
                 "supportedModelArch");
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
-            @NonNull String supportedModelArch) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
+            @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
         assertEquals("implementor", properties.implementor);
         assertEquals("description", properties.description);
         assertEquals(123, properties.version);
@@ -222,10 +241,9 @@
         assertEquals(456, properties.maxSoundModels);
         assertEquals(567, properties.maxKeyPhrases);
         assertEquals(678, properties.maxUsers);
-        assertEquals(RecognitionMode.GENERIC_TRIGGER
-                | RecognitionMode.USER_AUTHENTICATION
-                | RecognitionMode.USER_IDENTIFICATION
-                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+        assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
+                        | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
+                properties.recognitionModes);
         assertTrue(properties.captureTransition);
         assertEquals(321, properties.maxBufferMs);
         assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@
         config.phraseRecognitionExtras[0].levels[0].userId = 234;
         config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
         config.data = new byte[]{5, 4, 3, 2, 1};
-        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        config.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return config;
     }
 
@@ -295,13 +313,12 @@
             int captureHandle) {
         validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
 
-        assertEquals(AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+        assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+                config.audioCapabilities);
     }
 
     static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
-            int hwHandle,
-            int status) {
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
         halEvent.status = status;
@@ -351,8 +368,7 @@
         return event;
     }
 
-    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
-            int hwHandle,
+    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
             int status) {
         ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@
         PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
         extra.id = 123;
         extra.confidenceLevel = 52;
-        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
-                | RecognitionMode.GENERIC_TRIGGER;
+        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
         ConfidenceLevel level = new ConfidenceLevel();
         level.userId = 31;
         level.levelPercent = 43;
@@ -396,8 +411,8 @@
         return event;
     }
 
-    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
-    createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
         halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index b1b53fc..9e986be 100644
--- a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -65,7 +65,12 @@
         }
 
         @Override
-        long getMaxSatiatedCirculation() {
+        long getInitialSatiatedConsumptionLimit() {
+            return 0;
+        }
+
+        @Override
+        long getHardSatiatedConsumptionLimit() {
             return 0;
         }
 
@@ -102,7 +107,7 @@
         TrendCalculator trendCalculator = new TrendCalculator();
         mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
 
-        trendCalculator.reset(0, null);
+        trendCalculator.reset(0, 0, null);
         assertEquals("Expected not to cross lower threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -115,10 +120,10 @@
                 new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))),
                 mock(AffordabilityChangeListener.class), mEconomicPolicy));
         for (ActionAffordabilityNote note : affordabilityNotes) {
-            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
         }
 
-        trendCalculator.reset(1234, affordabilityNotes);
+        trendCalculator.reset(1234, 1234, affordabilityNotes);
         assertEquals("Expected not to cross lower threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossLowerThresholdMs());
@@ -133,32 +138,28 @@
 
         OngoingEvent[] events = new OngoingEvent[]{
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
-                        null, 1, 1),
+                        1, new EconomicPolicy.Cost(1, 4)),
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
-                        null, 2, 3),
-                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3",
-                        null, 3, -3),
+                        2, new EconomicPolicy.Cost(3, 6)),
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3", 3,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 0, 3, 3)),
         };
 
-        trendCalculator.reset(0, null);
+        trendCalculator.reset(0, 100, null);
         for (OngoingEvent event : events) {
             trendCalculator.accept(event);
         }
-        assertEquals("Expected not to cross lower threshold",
-                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
-                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(25_000, trendCalculator.getTimeToCrossLowerThresholdMs());
         assertEquals("Expected not to cross upper threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossUpperThresholdMs());
 
         ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
-        trendCalculator.reset(1234, affordabilityNotes);
+        trendCalculator.reset(1234, 1234, affordabilityNotes);
         for (OngoingEvent event : events) {
             trendCalculator.accept(event);
         }
-        assertEquals("Expected not to cross lower threshold",
-                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
-                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(308_000, trendCalculator.getTimeToCrossLowerThresholdMs());
         assertEquals("Expected not to cross upper threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossUpperThresholdMs());
@@ -174,18 +175,19 @@
                 new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))),
                 mock(AffordabilityChangeListener.class), mEconomicPolicy));
         for (ActionAffordabilityNote note : affordabilityNotes) {
-            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
         }
 
         // Balance is already above threshold and events are all positive delta.
         // There should be no time to report.
-        trendCalculator.reset(1234, affordabilityNotes);
+        trendCalculator.reset(1234, 1234, affordabilityNotes);
         trendCalculator.accept(
-                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
-                        null, 1, 1));
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
         trendCalculator.accept(
-                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
-                        null, 2, 3));
+                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+                                3, 3, 3)));
 
         assertEquals("Expected not to cross lower threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -196,16 +198,16 @@
 
         // Balance is already below threshold and events are all negative delta.
         // There should be no time to report.
-        trendCalculator.reset(1, affordabilityNotes);
+        trendCalculator.reset(1, 0, affordabilityNotes);
         trendCalculator.accept(
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
-                        null, 1, -1));
+                        1, new EconomicPolicy.Cost(1, 1)));
         trendCalculator.accept(
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
-                        null, 2, -3));
+                        2, new EconomicPolicy.Cost(3, 3)));
 
         assertEquals("Expected not to cross lower threshold",
-                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                0,
                 trendCalculator.getTimeToCrossLowerThresholdMs());
         assertEquals("Expected not to cross upper threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -213,7 +215,7 @@
     }
 
     @Test
-    public void testSimpleTrendToThreshold() {
+    public void testSimpleTrendToThreshold_Balance() {
         TrendCalculator trendCalculator = new TrendCalculator();
         mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
 
@@ -222,18 +224,19 @@
                 new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
                 mock(AffordabilityChangeListener.class), mEconomicPolicy));
         for (ActionAffordabilityNote note : affordabilityNotes) {
-            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
         }
 
         // Balance is below threshold and events are all positive delta.
         // Should report the correct time to the upper threshold.
-        trendCalculator.reset(0, affordabilityNotes);
+        trendCalculator.reset(0, 1000, affordabilityNotes);
         trendCalculator.accept(
-                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
-                        null, 1, 1));
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
         trendCalculator.accept(
-                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
-                        null, 2, 3));
+                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION,
+                                3, 3, 3)));
 
         assertEquals("Expected not to cross lower threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
@@ -242,13 +245,13 @@
 
         // Balance is above the threshold and events are all negative delta.
         // Should report the correct time to the lower threshold.
-        trendCalculator.reset(40, affordabilityNotes);
+        trendCalculator.reset(40, 100, affordabilityNotes);
         trendCalculator.accept(
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
-                        null, 1, -1));
+                        1, new EconomicPolicy.Cost(1, 1)));
         trendCalculator.accept(
                 new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
-                        null, 2, -3));
+                        2, new EconomicPolicy.Cost(3, 3)));
 
         assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs());
         assertEquals("Expected not to cross upper threshold",
@@ -257,6 +260,123 @@
     }
 
     @Test
+    public void testSelectCorrectThreshold_Balance() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10);
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is below threshold and events are all positive delta.
+        // Should report the correct time to the correct upper threshold.
+        trendCalculator.reset(0, 10_000, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
+
+        assertEquals("Expected not to cross lower threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+        // Balance is above the threshold and events are all negative delta.
+        // Should report the correct time to the correct lower threshold.
+        trendCalculator.reset(30, 500, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        1, new EconomicPolicy.Cost(1, 1)));
+
+        assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testTrendsToBothThresholds_Balance() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+        mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Balance is between both thresholds and events are mixed positive/negative delta.
+        // Should report the correct time to each threshold.
+        trendCalculator.reset(35, 10_000, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 3, 3, 3)));
+        trendCalculator.accept(
+                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, 2,
+                                2, 2)));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
+                        3, new EconomicPolicy.Cost(2, 2)));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
+                        4, new EconomicPolicy.Cost(3, 3)));
+
+        assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
+    public void testSimpleTrendToThreshold_ConsumptionLimit() {
+        TrendCalculator trendCalculator = new TrendCalculator();
+        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+
+        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+                mock(AffordabilityChangeListener.class), mEconomicPolicy));
+        for (ActionAffordabilityNote note : affordabilityNotes) {
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
+        }
+
+        // Events are all negative delta. Consumable credits will run out before app's balance.
+        // Should report the correct time to the lower threshold.
+        trendCalculator.reset(10000, 40, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        1, new EconomicPolicy.Cost(1, 10)));
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+                        2, new EconomicPolicy.Cost(3, 40)));
+
+        assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
+    }
+
+    @Test
     public void testSelectCorrectThreshold() {
         TrendCalculator trendCalculator = new TrendCalculator();
         mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
@@ -278,68 +398,45 @@
                 new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
                 mock(AffordabilityChangeListener.class), mEconomicPolicy));
         for (ActionAffordabilityNote note : affordabilityNotes) {
-            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+            note.recalculateCosts(mEconomicPolicy, 0, "com.test.app");
         }
 
-        // Balance is below threshold and events are all positive delta.
-        // Should report the correct time to the correct upper threshold.
-        trendCalculator.reset(0, affordabilityNotes);
+        // Balance is above threshold, consumable credits is 0, and events are all positive delta.
+        // There should be no time to the upper threshold since consumable credits is the limiting
+        // factor.
+        trendCalculator.reset(10_000, 0, affordabilityNotes);
         trendCalculator.accept(
-                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
-                        null, 1, 1));
+                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1,
+                        new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1)));
 
         assertEquals("Expected not to cross lower threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossLowerThresholdMs());
-        assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
-
-        // Balance is above the threshold and events are all negative delta.
-        // Should report the correct time to the correct lower threshold.
-        trendCalculator.reset(30, affordabilityNotes);
-        trendCalculator.accept(
-                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
-                        null, 1, -1));
-
-        assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
         assertEquals("Expected not to cross upper threshold",
                 TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
                 trendCalculator.getTimeToCrossUpperThresholdMs());
-    }
 
-    @Test
-    public void testTrendsToBothThresholds() {
-        TrendCalculator trendCalculator = new TrendCalculator();
-        mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
-        mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+        // Balance is above threshold, consumable credits is low, and events are all negative delta.
+        trendCalculator.reset(10_000, 4, affordabilityNotes);
+        trendCalculator.accept(
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        1, new EconomicPolicy.Cost(1, 10)));
 
-        ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
-        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
-                new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
-                mock(AffordabilityChangeListener.class), mEconomicPolicy));
-        affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
-                new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
-                mock(AffordabilityChangeListener.class), mEconomicPolicy));
-        for (ActionAffordabilityNote note : affordabilityNotes) {
-            note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
-        }
+        assertEquals(4000, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
 
-        // Balance is between both thresholds and events are mixed positive/negative delta.
-        // Should report the correct time to each threshold.
-        trendCalculator.reset(35, affordabilityNotes);
+        // Balance is above threshold, consumable credits is 0, and events are all negative delta.
+        // Time to the lower threshold should be 0 since consumable credits is already 0.
+        trendCalculator.reset(10_000, 0, affordabilityNotes);
         trendCalculator.accept(
-                new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
-                        null, 1, 3));
-        trendCalculator.accept(
-                new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
-                        null, 2, 2));
-        trendCalculator.accept(
-                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
-                        null, 3, -2));
-        trendCalculator.accept(
-                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
-                        null, 4, -3));
+                new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+                        1, new EconomicPolicy.Cost(1, 10)));
 
-        assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
-        assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+        assertEquals(0, trendCalculator.getTimeToCrossLowerThresholdMs());
+        assertEquals("Expected not to cross upper threshold",
+                TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+                trendCalculator.getTimeToCrossUpperThresholdMs());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
index 4a25323..22dcf84 100644
--- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -54,13 +54,13 @@
     @Test
     public void testMultipleTransactions() {
         final Ledger ledger = new Ledger();
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0));
         assertEquals(5, ledger.getCurrentBalance());
         assertEquals(5, ledger.get24HourSum(1, 60_000));
-        ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+        ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0));
         assertEquals(30, ledger.getCurrentBalance());
         assertEquals(30, ledger.get24HourSum(1, 60_000));
-        ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+        ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5));
         assertEquals(20, ledger.getCurrentBalance());
         assertEquals(20, ledger.get24HourSum(1, 60_000));
     }
@@ -68,13 +68,13 @@
     @Test
     public void test24HourSum() {
         final Ledger ledger = new Ledger();
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500, 0));
         assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
         ledger.recordTransaction(
-                new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+                new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500, 0));
         assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
         ledger.recordTransaction(
-                new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+                new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1, 0));
         assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
         assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
         assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
@@ -93,17 +93,17 @@
 
         final long now = getCurrentTimeMillis();
         Ledger.Transaction transaction1 = new Ledger.Transaction(
-                now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800);
+                now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0);
         Ledger.Transaction transaction2 = new Ledger.Transaction(
-                now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600);
+                now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0);
         Ledger.Transaction transaction3 = new Ledger.Transaction(
-                now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600);
+                now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0);
         // Instant event
         Ledger.Transaction transaction4 = new Ledger.Transaction(
-                now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500);
+                now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0);
         // Recent event
         Ledger.Transaction transaction5 = new Ledger.Transaction(
-                now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400);
+                now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400, 0);
         ledger.recordTransaction(transaction1);
         ledger.recordTransaction(transaction2);
         ledger.recordTransaction(transaction3);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 020d9f8..01e306e 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -61,8 +61,6 @@
 
 import androidx.test.InstrumentationRegistry;
 
-import com.android.internal.app.IBatteryStats;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -100,14 +98,12 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock
-    private VibrationThread.VibrationCallbacks mThreadCallbacks;
+    private VibrationThread.VibratorManagerHooks mManagerHooks;
     @Mock
     private VibratorController.OnVibrationCompleteListener mControllerCallbacks;
     @Mock
     private IBinder mVibrationToken;
     @Mock
-    private IBatteryStats mIBatteryStatsMock;
-    @Mock
     private VibrationConfig mVibrationConfigMock;
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -178,8 +174,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -197,8 +193,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -219,8 +215,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -252,8 +248,8 @@
         thread.cancel();
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
 
@@ -404,8 +400,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -427,8 +423,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibration);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -446,8 +442,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
-        verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+        verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+        verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
@@ -466,8 +462,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -486,8 +482,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
-        verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
+        verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
+        verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
@@ -536,8 +532,8 @@
         waitForCompletion(thread);
 
         // Use first duration the vibrator is turned on since we cannot estimate the clicks.
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -573,8 +569,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
@@ -648,9 +644,9 @@
                 VibrationEffect.createOneShot(10, 100)));
 
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
-        verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
-        verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any());
+        verify(mManagerHooks, never()).triggerSyncedVibration(anyLong());
+        verify(mManagerHooks, never()).cancelSyncedVibration();
     }
 
     @Test
@@ -666,8 +662,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
@@ -690,8 +686,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
@@ -728,8 +724,8 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
@@ -777,13 +773,13 @@
         controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
 
-        InOrder batterVerifier = inOrder(mIBatteryStatsMock);
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
-        batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        InOrder batteryVerifier = inOrder(mManagerHooks);
+        batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
+        batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
+        batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
+        batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
+        batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
 
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
         assertFalse(thread.getVibrators().get(1).isVibrating());
@@ -807,8 +803,8 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
 
         VibrationEffect composed = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
@@ -823,9 +819,9 @@
         waitForCompletion(thread);
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
 
         VibrationEffectSegment expected = expectedPrimitive(
@@ -840,8 +836,8 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(true);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(true);
 
         long vibrationId = 1;
         VibrationEffect composed = VibrationEffect.startComposition()
@@ -863,9 +859,9 @@
                 | IVibratorManager.CAP_MIXED_TRIGGER_ON
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM
                 | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
         verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
     }
 
@@ -875,7 +871,7 @@
         mockVibrators(vibratorIds);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
 
         long vibrationId = 1;
         CombinedVibration effect = CombinedVibration.startParallel()
@@ -886,9 +882,9 @@
         waitForCompletion(thread);
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks, never()).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks, never()).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).cancelSyncedVibration();
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
                 mVibratorProviders.get(1).getEffectSegments());
@@ -903,8 +899,8 @@
         int[] vibratorIds = new int[]{1, 2};
         mockVibrators(vibratorIds);
         mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
-        when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
-        when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(false);
+        when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+        when(mManagerHooks.triggerSyncedVibration(anyLong())).thenReturn(false);
 
         long vibrationId = 1;
         CombinedVibration effect = CombinedVibration.startParallel()
@@ -919,9 +915,9 @@
                 | IVibratorManager.CAP_PREPARE_PERFORM
                 | IVibratorManager.CAP_MIXED_TRIGGER_ON
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
-        verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
-        verify(mThreadCallbacks).cancelSyncedVibration();
+        verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks).cancelSyncedVibration();
         assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
     }
 
@@ -952,8 +948,8 @@
 
         waitForCompletion(thread);
 
-        verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(80L));
-        verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
+        verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L));
+        verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
@@ -1161,9 +1157,9 @@
         VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
 
         // Vibration completed but vibrator not yet released.
-        verify(mThreadCallbacks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
+        verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
                 eq(Vibration.Status.FINISHED));
-        verify(mThreadCallbacks, never()).onVibratorsReleased();
+        verify(mManagerHooks, never()).onVibrationThreadReleased();
 
         // Thread still running ramp down.
         assertTrue(thread.isAlive());
@@ -1177,9 +1173,9 @@
         waitForCompletion(thread);
 
         // Does not cancel already finished vibration, but releases vibrator.
-        verify(mThreadCallbacks, never()).onVibrationCompleted(eq(vibrationId),
+        verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
                 eq(Vibration.Status.CANCELLED));
-        verify(mThreadCallbacks).onVibratorsReleased();
+        verify(mManagerHooks).onVibrationThreadReleased();
     }
 
     @Test
@@ -1300,7 +1296,7 @@
 
     private VibrationThread startThreadAndDispatcher(Vibration vib) {
         VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
-                createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
+                createVibratorControllers(), mWakeLock, mManagerHooks);
         doAnswer(answer -> {
             thread.vibratorComplete(answer.getArgument(0));
             return null;
@@ -1380,8 +1376,8 @@
     }
 
     private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
-        verify(mThreadCallbacks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
-        verify(mThreadCallbacks).onVibratorsReleased();
+        verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
+        verify(mManagerHooks).onVibrationThreadReleased();
     }
 
     private final class TestLooperAutoDispatcher extends Thread {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 52975ef..19111e5 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -71,6 +71,7 @@
 import android.os.test.TestLooper;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
+import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
@@ -78,6 +79,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.LocalServices;
@@ -144,6 +146,8 @@
     private AppOpsManager mAppOpsManagerMock;
     @Mock
     private IInputManager mIInputManagerMock;
+    @Mock
+    private IBatteryStats mBatteryStatsMock;
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
 
@@ -152,12 +156,14 @@
     private FakeVibrator mVibrator;
     private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
     private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
+    private VibrationConfig mVibrationConfig;
 
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
+        mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -222,6 +228,11 @@
                     }
 
                     @Override
+                    IBatteryStats getBatteryStatsService() {
+                        return mBatteryStatsMock;
+                    }
+
+                    @Override
                     VibratorController createVibratorController(int vibratorId,
                             VibratorController.OnVibrationCompleteListener listener) {
                         return mVibratorProviders.get(vibratorId)
@@ -382,6 +393,11 @@
         inOrderVerifier.verify(listenerMock).onVibrating(eq(true));
         inOrderVerifier.verify(listenerMock).onVibrating(eq(false));
         inOrderVerifier.verifyNoMoreInteractions();
+
+        InOrder batteryVerifier = inOrder(mBatteryStatsMock);
+        batteryVerifier.verify(mBatteryStatsMock)
+                .noteVibratorOn(UID, 40 + mVibrationConfig.getRampDownDurationMs());
+        batteryVerifier.verify(mBatteryStatsMock).noteVibratorOff(UID);
     }
 
     @Test
@@ -731,6 +747,12 @@
         // Wait before checking it never played a second effect.
         assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1,
                 service, /* timeout= */ 50));
+
+        // The time estimate is recorded when the vibration starts, repeating vibrations
+        // are capped at BATTERY_STATS_REPEATING_VIBRATION_DURATION (=5000).
+        verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
+        // The second vibration shouldn't have recorded that the vibrators were turned on.
+        verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
     }
 
     @Test
@@ -765,6 +787,9 @@
         when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
         when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
         setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
+        // Mock alarm intensity equals to default value to avoid scaling in this test.
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
         VibratorManagerService service = createSystemReadyService();
 
         CombinedVibration effect = CombinedVibration.createParallel(
@@ -804,6 +829,9 @@
         mockVibrators(1, 2);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
         mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        // Mock alarm intensity equals to default value to avoid scaling in this test.
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
         VibratorManagerService service = createSystemReadyService();
         // The native callback will be dispatched manually in this test.
         mTestLooper.stopAutoDispatchAndIgnoreExceptions();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index 2794d48..c64ff9e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -114,7 +114,7 @@
     }
 
     @Test
-    public void testSensorChanged_normalCase2() {
+    public void testOnSensorChanged_normalCase2() {
         mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
 
         mFakeRotationResolverInternal.callbackWithFailureResult(
@@ -123,6 +123,15 @@
         assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
     }
 
+    @Test
+    public void testOnSensorChanged_rotationResolverServiceIsNull_useSensorResult() {
+        mWindowOrientationListener.mRotationResolverService = null;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
+    }
+
     static final class TestableRotationResolver extends RotationResolverInternal {
         @Surface.Rotation
         RotationResolverCallbackInternal mCallback;
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index a12bc3b..de81d6b 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -28,6 +28,8 @@
 import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
 
+import static com.android.server.UiModeManagerService.SUPPORTED_NIGHT_MODE_CUSTOM_TYPES;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.TestCase.assertFalse;
@@ -295,6 +297,24 @@
     }
 
     @Test
+    public void setNightModeCustomType_customTypeUnknown_shouldThrow() throws RemoteException {
+        assertThrows(IllegalArgumentException.class,
+                () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN));
+    }
+
+    @Test
+    public void setNightModeCustomType_customTypeUnsupported_shouldThrow() throws RemoteException {
+        assertThrows(IllegalArgumentException.class,
+                () -> {
+                    int maxSupportedCustomType = 0;
+                    for (Integer supportedType : SUPPORTED_NIGHT_MODE_CUSTOM_TYPES) {
+                        maxSupportedCustomType = Math.max(maxSupportedCustomType, supportedType);
+                    }
+                    mService.setNightModeCustomType(maxSupportedCustomType + 1);
+                });
+    }
+
+    @Test
     public void setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered()
             throws RemoteException {
         try {
@@ -777,7 +797,6 @@
     }
 
 
-
     @Test
     public void customTime_darkThemeOn_beforeStartEnd() throws RemoteException {
         LocalTime now = LocalTime.now();
@@ -1220,6 +1239,35 @@
         verify(listener, never()).onProjectionStateChanged(anyInt(), any());
     }
 
+    @Test
+    public void enableCarMode_failsForBogusPackageName() throws Exception {
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+            .thenReturn(TestInjector.CALLING_UID + 1);
+
+        assertThrows(SecurityException.class, () -> mService.enableCarMode(0, 0, PACKAGE_NAME));
+        assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
+    }
+
+    @Test
+    public void disableCarMode_failsForBogusPackageName() throws Exception {
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+            .thenReturn(TestInjector.CALLING_UID);
+        mService.enableCarMode(0, 0, PACKAGE_NAME);
+        assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+            .thenReturn(TestInjector.CALLING_UID + 1);
+
+        assertThrows(SecurityException.class,
+            () -> mService.disableCarModeByCallingPackage(0, PACKAGE_NAME));
+        assertThat(mService.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_CAR);
+
+        // Clean up
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+            .thenReturn(TestInjector.CALLING_UID);
+         mService.disableCarModeByCallingPackage(0, PACKAGE_NAME);
+        assertThat(mService.getCurrentModeType()).isNotEqualTo(Configuration.UI_MODE_TYPE_CAR);
+    }
+
     private void requestAllPossibleProjectionTypes() throws RemoteException {
         for (int i = 0; i < Integer.SIZE; ++i) {
             mService.requestProjection(mBinder, 1 << i, PACKAGE_NAME);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
index 1126e1e..4b6183d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -15,16 +15,22 @@
  */
 package com.android.server.notification;
 
+import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
+import static android.os.UserHandle.USER_NULL;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -35,6 +41,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -51,6 +58,8 @@
     private static final int SIZE = 5;
 
     private NotificationManagerService.Archive mArchive;
+    @Mock
+    private UserManager mUm;
 
     @Before
     public void setUp() {
@@ -59,6 +68,9 @@
         mArchive = new NotificationManagerService.Archive(SIZE);
         mArchive.updateHistoryEnabled(USER_SYSTEM, true);
         mArchive.updateHistoryEnabled(USER_CURRENT, true);
+
+        when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(
+                new int[] {USER_CURRENT, USER_SYSTEM});
     }
 
     private StatusBarNotification getNotification(String pkg, int id, UserHandle user) {
@@ -70,7 +82,6 @@
                 pkg, pkg, id, null, 0, 0, n, user, null, System.currentTimeMillis());
     }
 
-
     @Test
     public void testRecordAndRead() {
         List<String> expected = new ArrayList<>();
@@ -81,7 +92,7 @@
             mArchive.record(sbn, REASON_CANCEL);
         }
 
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
@@ -89,6 +100,22 @@
     }
 
     @Test
+    public void testCrossUser() {
+        mArchive.record(getNotification("pkg", 1, UserHandle.of(USER_SYSTEM)), REASON_CANCEL);
+        mArchive.record(getNotification("pkg", 2, UserHandle.of(USER_CURRENT)), REASON_CANCEL);
+        mArchive.record(getNotification("pkg", 3, UserHandle.of(USER_ALL)), REASON_CANCEL);
+        mArchive.record(getNotification("pkg", 4, UserHandle.of(USER_NULL)), REASON_CANCEL);
+
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
+        assertThat(actual).hasSize(3);
+        for (StatusBarNotification sbn : actual) {
+            if (sbn.getUserId() == USER_NULL) {
+                fail("leaked notification from wrong user");
+            }
+        }
+    }
+
+    @Test
     public void testRecordAndRead_overLimit() {
         List<String> expected = new ArrayList<>();
         for (int i = 0; i < (SIZE * 2); i++) {
@@ -99,7 +126,8 @@
             }
         }
 
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray((SIZE * 2), true));
+        List<StatusBarNotification> actual = Arrays.asList(
+                mArchive.getArray(mUm, (SIZE * 2), true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
@@ -119,7 +147,7 @@
             }
         }
 
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
@@ -140,7 +168,7 @@
         }
         mArchive.updateHistoryEnabled(USER_CURRENT, false);
 
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
@@ -165,7 +193,7 @@
         }
         mArchive.removeChannelNotifications("pkg", USER_CURRENT, "test0");
         mArchive.removeChannelNotifications("pkg", USER_CURRENT, "test" + (SIZE - 2));
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
@@ -215,7 +243,7 @@
             fail("Concurrent modification exception");
         }
 
-        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
         assertThat(actual).hasSize(expected.size());
         for (StatusBarNotification sbn : actual) {
             assertThat(expected).contains(sbn.getKey());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index dfcab2b..018a916 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -430,6 +430,7 @@
         when(mPermissionPolicyInternal.canShowPermissionPromptForTask(
                 any(ActivityManager.RecentTaskInfo.class))).thenReturn(false);
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
+        when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
 
         ActivityManager.AppTask task = mock(ActivityManager.AppTask.class);
         List<ActivityManager.AppTask> taskList = new ArrayList<>();
@@ -7643,8 +7644,9 @@
         waitForIdle();
 
         // A notification exists for the given record
-        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsBefore.length);
+        List<StatusBarNotification> notifsBefore =
+                mBinderService.getAppActiveNotifications(PKG, nr.getSbn().getUserId()).getList();
+        assertEquals(1, notifsBefore.size());
 
         reset(mPackageManager);
 
@@ -9131,4 +9133,33 @@
         // make sure we don't bother if the migration is not enabled
         assertThat(mService.getAllUsersNotificationPermissions()).isNull();
     }
+
+    @Test
+    public void testGetActiveNotification_filtersUsers() throws Exception {
+        when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0, 10});
+
+        NotificationRecord nr0 =
+                generateNotificationRecord(mTestNotificationChannel, 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag0",
+                nr0.getSbn().getId(), nr0.getSbn().getNotification(), nr0.getSbn().getUserId());
+
+        NotificationRecord nr10 =
+                generateNotificationRecord(mTestNotificationChannel, 10);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag10",
+                nr10.getSbn().getId(), nr10.getSbn().getNotification(), nr10.getSbn().getUserId());
+
+        NotificationRecord nr11 =
+                generateNotificationRecord(mTestNotificationChannel, 11);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag11",
+                nr11.getSbn().getId(), nr11.getSbn().getNotification(), nr11.getSbn().getUserId());
+        waitForIdle();
+
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertEquals(2, notifs.length);
+        for (StatusBarNotification sbn : notifs) {
+            if (sbn.getUserId() == 11) {
+                fail("leaked data across users");
+            }
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index d922f40..5a6ca6d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -293,6 +293,7 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
+        when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 184ea52..fe1ea0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -33,6 +33,7 @@
 import android.app.ActivityOptions;
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +41,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.util.Rational;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
 
@@ -91,6 +93,20 @@
     }
 
     @Test
+    public void testMakeLaunchIntoPip() {
+        // Construct some params with set values
+        PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .setAspectRatio(new Rational(1, 1))
+                .build();
+        // Construct ActivityOptions via makeLaunchIntoPip
+        ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+
+        // Verify the params in ActivityOptions has the right flag being turned on
+        assertNotNull(opts.getLaunchIntoPipParams());
+        assertTrue(opts.isLaunchIntoPip());
+    }
+
+    @Test
     public void testTransferLaunchCookie() {
         final Binder cookie = new Binder();
         final ActivityOptions options = ActivityOptions.makeBasic();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 043bc07..b3d6b3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -109,6 +109,7 @@
 
 import android.app.ActivityOptions;
 import android.app.ICompatCameraControlCallback;
+import android.app.PictureInPictureParams;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.DestroyActivityItem;
@@ -2200,6 +2201,20 @@
         assertFalse(activity.supportsPictureInPicture());
     }
 
+    @Test
+    public void testLaunchIntoPip() {
+        final PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .build();
+        final ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setLaunchIntoPipActivityOptions(opts)
+                .build();
+
+        // Verify the pictureInPictureArgs is set on the new Activity
+        assertNotNull(activity.pictureInPictureArgs);
+        assertTrue(activity.pictureInPictureArgs.isLaunchIntoPip());
+    }
+
     private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
             boolean shouldUpdate, boolean activityChange) {
         reset(activity.app);
@@ -3044,7 +3059,7 @@
         mDisplayContent.setImeLayeringTarget(app);
         mDisplayContent.updateImeInputAndControlTarget(app);
 
-        InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+        InsetsState state = app.getInsetsState();
         assertFalse(state.getSource(ITYPE_IME).isVisible());
         assertTrue(state.getSource(ITYPE_IME).getFrame().isEmpty());
 
@@ -3064,7 +3079,7 @@
 
         // Verify when IME is visible and the app can receive the right IME insets from policy.
         makeWindowVisibleAndDrawn(app, mImeWindow);
-        state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+        state = app.getInsetsState();
         assertTrue(state.getSource(ITYPE_IME).isVisible());
         assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame());
     }
@@ -3076,7 +3091,7 @@
         final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
         final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
 
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mImeWindow, null, null);
         mImeWindow.getControllableInsetProvider().setServerVisible(true);
 
@@ -3110,7 +3125,7 @@
         assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
         verify(app2.mClient, atLeastOnce()).insetsChanged(insetsStateCaptor.capture(), anyBoolean(),
                 anyBoolean());
-        assertFalse(insetsStateCaptor.getAllValues().get(0).peekSource(ITYPE_IME).isVisible());
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c8e48a4..87abc53 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -39,6 +39,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Process.SYSTEM_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -68,6 +69,7 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1141,6 +1143,34 @@
     }
 
     @Test
+    public void testStartActivityInner_inTaskFragment_allowedForSystemUid() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID,
+                "system_uid");
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertTrue(taskFragment.hasChild());
+    }
+
+    @Test
     public void testStartActivityInner_inTaskFragment_allowedForSameUid() {
         final ActivityStarter starter = prepareStarter(0, false);
         final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
@@ -1264,4 +1294,36 @@
         // Verify the cookie is updated
         assertTrue(mRootWindowContainer.topRunningActivity().mLaunchCookie == newCookie);
     }
+
+    @Test
+    public void testStartLaunchIntoPipActivity() {
+        final ActivityStarter starter = prepareStarter(0, false);
+
+        // Create an activity from ActivityOptions#makeLaunchIntoPip
+        final PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .build();
+        final ActivityOptions opts = ActivityOptions.makeLaunchIntoPip(params);
+        ActivityRecord targetRecord = new ActivityBuilder(mAtm)
+                .setLaunchIntoPipActivityOptions(opts)
+                .build();
+
+        // Start the target launch-into-pip activity from a source
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        starter.startActivityInner(
+                /* r */ targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */ null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */ true,
+                /* options */ opts,
+                /* inTask */ null,
+                /* inTaskFragment */ null,
+                /* restrictedBgActivity */ false,
+                /* intentGrants */ null);
+
+        // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord.
+        assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull();
+        assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 3d89805..c21a5b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.window.BackNavigationInfo.typeToString;
 
@@ -117,6 +118,9 @@
     private Task createTopTaskWithActivity() {
         Task task = createTask(mDefaultDisplay);
         ActivityRecord record = createActivityRecord(task);
+        // enable OnBackInvokedCallbacks
+        record.info.applicationInfo.privateFlagsExt |=
+                PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
         createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
         when(record.mSurfaceControl.isValid()).thenReturn(true);
         mAtm.setFocusedTask(task.mTaskId, record);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 3beb7f2..88c7017 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -283,6 +283,21 @@
         verify(mTransaction).remove(dimLayer);
     }
 
+    @Test
+    public void testDimmerWithBlurUpdatesTransaction() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
+        mHost.addChild(child, 0);
+
+        final int blurRadius = 50;
+        mDimmer.dimBelow(mTransaction, child, 0, blurRadius);
+        SurfaceControl dimLayer = getDimLayer();
+
+        assertNotNull("Dimmer should have created a surface", dimLayer);
+
+        verify(mTransaction).setBackgroundBlurRadius(dimLayer, blurRadius);
+        verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1);
+    }
+
     private SurfaceControl getDimLayer() {
         return mDimmer.mDimState.mDimLayer;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 497ae1d..db22757 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -258,11 +258,9 @@
                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
         mWindow.mAboveInsetsState.set(
                 mDisplayContent.getInsetsStateController().getRawInsetsState());
-        final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
-        final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
 
         assertEquals(DISPLAY_WIDTH, frame.width());
         assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 5f96267..ca8481a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -71,7 +71,7 @@
     public void testIsImeShowing() {
         WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
         makeWindowVisibleAndDrawn(ime);
-        mImeProvider.setWindow(ime, null, null);
+        mImeProvider.setWindowContainer(ime, null, null);
 
         WindowState target = createWindow(null, TYPE_APPLICATION, "app");
         mDisplayContent.setImeLayeringTarget(target);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2eece4c..e387615 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -68,11 +68,14 @@
         // IME cannot be the IME target.
         ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
-        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_IME));
-        assertNull(getController().getInsetsForWindow(navBar).peekSource(ITYPE_STATUS_BAR));
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
+
+        assertNull(navBar.getInsetsState().peekSource(ITYPE_IME));
+        assertNull(navBar.getInsetsState().peekSource(ITYPE_STATUS_BAR));
     }
 
     @Test
@@ -81,13 +84,15 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_PINNED);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_IME));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_IME));
     }
 
     @Test
@@ -96,12 +101,14 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_FREEFORM);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @Test
@@ -110,19 +117,21 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         app.setAlwaysOnTop(true);
 
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_STATUS_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_independentSources() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
         final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
@@ -130,34 +139,34 @@
         app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
-        assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
+        assertTrue(app1.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_belowIme() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
         app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_aboveIme() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+        assertFalse(app.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
@@ -172,7 +181,7 @@
 
         // Make IME and stay visible during the test.
         mImeWindow.setHasSurface(true);
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
         getController().onImeControlTargetChanged(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_IME, true);
@@ -195,7 +204,7 @@
 
         // app won't get visible IME insets while above IME even when IME is visible.
         assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
+        assertFalse(app.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
 
         // Reset invocation counter.
@@ -212,13 +221,13 @@
         verify(app, atLeastOnce()).notifyInsetsChanged();
 
         // app will get visible IME insets while below IME.
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_childWindow_altFocusable() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
@@ -231,15 +240,15 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(child.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
     @Test
     public void testStripForDispatch_childWindow_splitScreen() {
-        getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
@@ -252,8 +261,8 @@
         mDisplayContent.applySurfaceChangesTransaction();
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(child.getInsetsState().getSource(ITYPE_IME)
                 .isVisible());
     }
 
@@ -265,16 +274,16 @@
         // IME cannot be the IME target.
         ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
-        InsetsSourceProvider statusBarProvider =
+        WindowContainerInsetsSourceProvider statusBarProvider =
                 getController().getSourceProvider(ITYPE_STATUS_BAR);
-        statusBarProvider.setWindow(statusBar, null, ((displayFrames, windowState, rect) ->
-                        rect.set(0, 1, 2, 3)));
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+        statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) ->
+                rect.set(0, 1, 2, 3)));
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
         statusBar.setControllableInsetProvider(statusBarProvider);
 
         statusBarProvider.onPostLayout();
 
-        final InsetsState state = getController().getInsetsForWindow(ime);
+        final InsetsState state = ime.getInsetsState();
         assertEquals(new Rect(0, 1, 2, 3), state.getSource(ITYPE_STATUS_BAR).getFrame());
     }
 
@@ -285,10 +294,14 @@
         final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar");
         final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
-        getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null);
-        getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null,
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindowContainer(climateBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindowContainer(
+                extraNavBar, null,
                 null);
         getController().onBarControlTargetChanged(app, null, app, null);
         InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
@@ -299,7 +312,8 @@
     public void testControlRevoked() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         getController().onBarControlTargetChanged(null, null, null, null);
@@ -310,7 +324,8 @@
     public void testControlRevoked_animation() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
         getController().onBarControlTargetChanged(app, null, null, null);
         assertNotNull(getController().getControlsForDispatch(app));
         statusBar.cancelAnimation();
@@ -321,8 +336,9 @@
     public void testTransientVisibilityOfFixedRotationState() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
-        provider.setWindow(statusBar, null, null);
+        final WindowContainerInsetsSourceProvider provider = getController()
+                .getSourceProvider(ITYPE_STATUS_BAR);
+        provider.setWindowContainer(statusBar, null, null);
 
         final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
                 true /* copySources */);
@@ -344,13 +360,14 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
 
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
 
-        getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
@@ -365,13 +382,15 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
 
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
 
-        getController().updateAboveInsetsState(app, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
@@ -386,13 +405,14 @@
         final WindowState statusBar = createTestWindow("statusBar");
         final WindowState navBar = createTestWindow("navBar");
 
-        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+        getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
         getController().getSourceProvider(ITYPE_IME).setClientVisible(true);
-        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
-        getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
-        getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
-        getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindowContainer(statusBar, null,
+                null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
+
+        getController().updateAboveInsetsState(false /* notifyInsetsChange */);
 
         // ime is below others.
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
@@ -402,7 +422,7 @@
         assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
 
         ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */);
-        getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         // ime is above others.
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
@@ -420,11 +440,12 @@
     @Test
     public void testDispatchGlobalInsets() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
-        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+        getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
+                null);
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
         app.mAttrs.receiveInsetsIgnoringZOrder = true;
-        assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+        assertNotNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR));
     }
 
     private WindowState createTestWindow(String name) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index ee17f52..ba65104 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -251,7 +251,8 @@
         ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
+                null /* launchIntoPipHostActivity */, "initialMove");
 
         final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
         Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -260,7 +261,8 @@
         ensureTaskPlacement(fullscreenTask, secondActivity);
 
         // Move second activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
+                null /* launchIntoPipHostActivity */, "secondMove");
 
         // Need to get root tasks again as a new instance might have been created.
         pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -291,7 +293,8 @@
 
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
+                null /* launchIntoPipHostActivity */, "initialMove");
 
         assertTrue(firstActivity.mRequestForceTransition);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b815c38..7b38a95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
@@ -2128,6 +2129,136 @@
                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
     }
 
+    @Test
+    public void testIsEligibleForLetterboxEducation_educationNotEnabled_returnsFalse() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_notEligibleForFixedOrientation_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_windowingModeMultiWindow_returnsFalse() {
+        // Support non resizable in multi window
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        setUpDisplaySizeWithApp(1000, 1200);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+        // Non-resizable landscape activity
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP,
+                false /*moveParents*/, "test");
+        organizer.mPrimary.setBounds(0, 0, 1000, 600);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_fixedOrientationLandscape_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindow_returnsFalseUntilRemoved() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation returns
+        // true and mTask.dispatchTaskInfoChangedIfNeeded is called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindowAndEducationNotEnabled() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation still
+        // returns false and mTask.dispatchTaskInfoChangedIfNeeded isn't called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask, never()).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_letterboxedForFixedOrientation_returnsTrue() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_sizeCompatAndEligibleForFixedOrientation() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
     /**
      * Tests that all three paths in which aspect ratio logic can be applied yield the same
      * result, which is that aspect ratio is respected on app bounds. The three paths are
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
similarity index 91%
rename from services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
index 2987f94..10e4292 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
@@ -42,19 +42,19 @@
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class InsetsSourceProviderTest extends WindowTestsBase {
+public class WindowContainerInsetsSourceProviderTest extends WindowTestsBase {
 
     private InsetsSource mSource = new InsetsSource(ITYPE_STATUS_BAR);
-    private InsetsSourceProvider mProvider;
+    private WindowContainerInsetsSourceProvider mProvider;
     private InsetsSource mImeSource = new InsetsSource(ITYPE_IME);
-    private InsetsSourceProvider mImeProvider;
+    private WindowContainerInsetsSourceProvider mImeProvider;
 
     @Before
     public void setUp() throws Exception {
         mSource.setVisible(true);
-        mProvider = new InsetsSourceProvider(mSource,
+        mProvider = new WindowContainerInsetsSourceProvider(mSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
-        mImeProvider = new InsetsSourceProvider(mImeSource,
+        mImeProvider = new WindowContainerInsetsSourceProvider(mImeSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
     }
 
@@ -63,7 +63,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
         assertEquals(Insets.of(0, 100, 0, 0),
@@ -80,7 +80,7 @@
         ime.mGivenContentInsets.set(0, 0, 0, 60);
         ime.mGivenVisibleInsets.set(0, 0, 0, 75);
         ime.mHasSurface = true;
-        mProvider.setWindow(ime, null, null);
+        mProvider.setWindowContainer(ime, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
         assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
@@ -95,10 +95,10 @@
     public void testPostLayout_invisible() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
-                        false /* ignoreVisibility */));
+                false /* ignoreVisibility */));
     }
 
     @Test
@@ -106,7 +106,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar,
+        mProvider.setWindowContainer(statusBar,
                 (displayFrames, windowState, rect) -> {
                     rect.set(10, 10, 20, 20);
                 }, null);
@@ -126,7 +126,7 @@
         assertNull(mProvider.getControlTarget());
 
         // We can have the control or the control target after we have the insets source window.
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
@@ -164,7 +164,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForFakeTarget(target);
         assertNotNull(mProvider.getControl(target));
         assertNull(mProvider.getControl(target).getLeash());
@@ -178,7 +178,7 @@
 
         inputMethod.getFrame().set(new Rect(0, 400, 500, 500));
 
-        mImeProvider.setWindow(inputMethod, null, null);
+        mImeProvider.setWindowContainer(inputMethod, null, null);
         mImeProvider.setServerVisible(false);
         mImeSource.setVisible(true);
         mImeProvider.updateSourceFrame();
@@ -201,7 +201,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
@@ -215,7 +215,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
         target.setRequestedVisibilities(requestedVisibilities);
@@ -228,7 +228,7 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
-        mProvider.setWindow(statusBar, null, null);
+        mProvider.setWindowContainer(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
         // Still apply top insets if window overlaps even if it's top doesn't exactly match
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 41a59eb..a442de5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_2;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -68,6 +70,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
+import android.view.InsetsSource;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -478,7 +481,7 @@
                 windowState.mSurfaceAnimator).getAnimationType();
         assertTrue(parent.isAnimating(CHILDREN));
 
-        windowState.setControllableInsetProvider(mock(InsetsSourceProvider.class));
+        windowState.setControllableInsetProvider(mock(WindowContainerInsetsSourceProvider.class));
         assertFalse(parent.isAnimating(CHILDREN));
     }
 
@@ -1267,6 +1270,140 @@
         verify(prevSnapshot).destroy(t);
     }
 
+    @Test
+    public void testAddLocalInsetsSourceProvider() {
+         /*
+                ___ rootTask _______________________________________________
+               |        |                |                                  |
+          activity0    container     navigationBarInsetsProvider1    navigationBarInsetsProvider2
+                       /       \
+               activity1    activity2
+         */
+        final Task rootTask = createTask(mDisplayContent);
+
+        final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(rootTask, 0 /* userId */));
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs.setTitle("AppWindow0");
+        activity0.addWindow(createWindowState(attrs, activity0));
+
+        final Task container = createTaskInRootTask(rootTask, 0);
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs1.setTitle("AppWindow1");
+        activity1.addWindow(createWindowState(attrs1, activity1));
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs2.setTitle("AppWindow2");
+        activity2.addWindow(createWindowState(attrs2, activity2));
+        Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+        Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+        rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+
+        InsetsSource navigationBarInsetsProvider1Source = new InsetsSource(
+                ITYPE_LOCAL_NAVIGATION_BAR_1);
+        navigationBarInsetsProvider1Source.setFrame(navigationBarInsetsRect1);
+        navigationBarInsetsProvider1Source.setVisible(true);
+        InsetsSource navigationBarInsetsProvider2Source = new InsetsSource(
+                ITYPE_LOCAL_NAVIGATION_BAR_2);
+        navigationBarInsetsProvider2Source.setFrame(navigationBarInsetsRect2);
+        navigationBarInsetsProvider2Source.setVisible(true);
+
+        activity0.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+        }, true);
+        activity1.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+        activity2.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+    }
+
+    @Test
+    public void testRemoveLocalInsetsSourceProvider() {
+         /*
+                ___ rootTask _______________________________________________
+               |        |                |                                  |
+          activity0    container    navigationBarInsetsProvider1     navigationBarInsetsProvider2
+                       /       \
+               activity1    activity2
+         */
+        final Task rootTask = createTask(mDisplayContent);
+
+        final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(rootTask, 0 /* userId */));
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs.setTitle("AppWindow0");
+        activity0.addWindow(createWindowState(attrs, activity0));
+
+        final Task container = createTaskInRootTask(rootTask, 0);
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs1.setTitle("AppWindow1");
+        activity1.addWindow(createWindowState(attrs1, activity1));
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs2.setTitle("AppWindow2");
+        activity2.addWindow(createWindowState(attrs2, activity2));
+
+        activity2.addWindow(createWindowState(attrs2, activity2));
+        Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+        Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+        rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+        mDisplayContent.getInsetsStateController().onPostLayout();
+        rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        mDisplayContent.getInsetsStateController().onPostLayout();
+
+        activity0.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+        }, true);
+        activity1.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+        activity2.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 80192f7..ef600f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -105,7 +105,7 @@
  * Tests for the {@link WindowState} class.
  *
  * Build/Install/Run:
- *  atest WmTests:WindowStateTests
+ * atest WmTests:WindowStateTests
  */
 @SmallTest
 @Presubmit
@@ -411,7 +411,7 @@
         assertFalse(app.canAffectSystemUiFlags());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_STATUS_BAR })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_STATUS_BAR})
     @Test
     public void testVisibleWithInsetsProvider() {
         final WindowState statusBar = mStatusBarWindow;
@@ -419,7 +419,8 @@
         statusBar.mHasSurface = true;
         assertTrue(statusBar.isVisible());
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
+                .setWindowContainer(statusBar, null /* frameProvider */,
+                        null /* imeFrameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
@@ -623,7 +624,7 @@
         assertEquals(w.getWindowConfiguration().getBounds(), unscaledClientBounds);
     }
 
-    @UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
+    @UseTestDisplay(addWindows = {W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE})
     @Test
     public void testRequestDrawIfNeeded() {
         final WindowState startingApp = createWindow(null /* parent */,
@@ -831,7 +832,7 @@
         assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
     @Test
     public void testNeedsRelativeLayeringToIme_startingWindow() {
         WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
@@ -864,7 +865,7 @@
         verify(app).notifyInsetsChanged();
     }
 
-    @UseTestDisplay(addWindows = { W_INPUT_METHOD, W_ACTIVITY })
+    @UseTestDisplay(addWindows = {W_INPUT_METHOD, W_ACTIVITY})
     @Test
     public void testImeAlwaysReceivesVisibleNavigationBarInsets() {
         final InsetsSource navSource = new InsetsSource(ITYPE_NAVIGATION_BAR);
@@ -890,7 +891,7 @@
         mDisplayContent.mInputMethodWindow = imeWindow;
 
         final InsetsStateController controller = mDisplayContent.getInsetsStateController();
-        controller.getImeSourceProvider().setWindow(imeWindow, null, null);
+        controller.getImeSourceProvider().setWindowContainer(imeWindow, null, null);
 
         // Simulate app requests IME with updating all windows Insets State when IME is above app.
         mDisplayContent.setImeLayeringTarget(app);
@@ -898,7 +899,7 @@
         assertTrue(mDisplayContent.shouldImeAttachedToApp());
         controller.getImeSourceProvider().scheduleShowImePostLayout(app);
         controller.getImeSourceProvider().getSource().setVisible(true);
-        controller.updateAboveInsetsState(imeWindow, false);
+        controller.updateAboveInsetsState(false);
 
         // Expect all app windows behind IME can receive IME insets visible.
         assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
@@ -914,7 +915,7 @@
         assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY })
+    @UseTestDisplay(addWindows = {W_ACTIVITY})
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
         WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
@@ -940,7 +941,7 @@
         assertEquals(mAppWindow, mDisplayContent.getImeTarget(IME_TARGET_CONTROL).getWindow());
     }
 
-    @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
+    @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE})
     @Test
     public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
         WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
@@ -954,7 +955,7 @@
         mNotificationShadeWindow.setHasSurface(true);
         mNotificationShadeWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
         assertTrue(mNotificationShadeWindow.canBeImeTarget());
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mImeWindow, null, null);
 
         mDisplayContent.computeImeTarget(true);
@@ -963,8 +964,7 @@
                 .setSourceVisible(ITYPE_IME, true);
 
         // Verify notificationShade can still get IME insets even windowing mode is multi-window.
-        InsetsState state = mDisplayContent.getInsetsStateController().getInsetsForWindow(
-                mNotificationShadeWindow);
+        InsetsState state = mNotificationShadeWindow.getInsetsState();
         assertNotNull(state.peekSource(ITYPE_IME));
         assertTrue(state.getSource(ITYPE_IME).isVisible());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5dbb4c5..bd1f9d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -941,6 +941,7 @@
         private boolean mOnTop = false;
         private ActivityInfo.WindowLayout mWindowLayout;
         private boolean mVisible = true;
+        private ActivityOptions mLaunchIntoPipOpts;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -1076,6 +1077,11 @@
             return this;
         }
 
+        ActivityBuilder setLaunchIntoPipActivityOptions(ActivityOptions opts) {
+            mLaunchIntoPipOpts = opts;
+            return this;
+        }
+
         ActivityRecord build() {
             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
             try {
@@ -1132,7 +1138,9 @@
             }
 
             ActivityOptions options = null;
-            if (mLaunchTaskBehind) {
+            if (mLaunchIntoPipOpts != null) {
+                options = mLaunchIntoPipOpts;
+            } else if (mLaunchTaskBehind) {
                 options = ActivityOptions.makeTaskLaunchBehind();
             }
             final ActivityRecord activity = new ActivityRecord.Builder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 74cff10..4b5f330a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -262,7 +262,7 @@
     @Test
     public void testSetInsetsFrozen_notAffectImeWindowState() {
         // Pre-condition: make the IME window be controlled by IME insets provider.
-        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindowContainer(
                 mDisplayContent.mInputMethodWindow, null, null);
 
         // Simulate an app window to be the IME layering target, assume the app window has no
diff --git a/services/translation/OWNERS b/services/translation/OWNERS
index a1e663a..440f9a8 100644
--- a/services/translation/OWNERS
+++ b/services/translation/OWNERS
@@ -1,8 +1,3 @@
 # Bug component: 994311
 
-adamhe@google.com
-augale@google.com
-joannechung@google.com
-lpeter@google.com
-svetoslavganov@google.com
-tymtsai@google.com
+include /core/java/android/view/translation/OWNERS
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 9f4fee8..364d592 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -24,6 +24,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -43,6 +44,7 @@
 import android.view.translation.TranslationCapability;
 import android.view.translation.TranslationContext;
 import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationController;
 import android.view.translation.UiTranslationManager.UiTranslationState;
 import android.view.translation.UiTranslationSpec;
 
@@ -253,7 +255,10 @@
             try (TransferPipe tp = new TransferPipe()) {
                 activityTokens.getApplicationThread().dumpActivity(tp.getWriteFd(),
                         activityTokens.getActivityToken(), prefix,
-                        new String[]{"--translation"});
+                        new String[] {
+                                Activity.DUMP_ARG_DUMP_DUMPABLE,
+                                UiTranslationController.DUMPABLE_NAME
+                        });
                 tp.go(fd);
             } catch (IOException e) {
                 pw.println(prefix + "Failure while dumping the activity: " + e);
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index 27e8d69..ab8f69b 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -22,6 +22,7 @@
 
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -39,6 +40,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 
 class BroadcastResponseStatsTracker {
     private static final String TAG = "ResponseStatsTracker";
@@ -172,28 +175,27 @@
         }
     }
 
-    @NonNull BroadcastResponseStats queryBroadcastResponseStats(int callingUid,
-            @NonNull String packageName, long id, @UserIdInt int userId) {
-        final BroadcastResponseStats aggregatedResponseStats =
-                new BroadcastResponseStats(packageName);
+    @NonNull List<BroadcastResponseStats> queryBroadcastResponseStats(int callingUid,
+            @Nullable String packageName, @IntRange(from = 0) long id, @UserIdInt int userId) {
+        final List<BroadcastResponseStats> broadcastResponseStatsList = new ArrayList<>();
         synchronized (mLock) {
             final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
                     mUserResponseStats.get(callingUid);
             if (responseStatsForCaller == null) {
-                return aggregatedResponseStats;
+                return broadcastResponseStatsList;
             }
             final UserBroadcastResponseStats responseStatsForUser =
                     responseStatsForCaller.get(userId);
             if (responseStatsForUser == null) {
-                return aggregatedResponseStats;
+                return broadcastResponseStatsList;
             }
-            responseStatsForUser.aggregateBroadcastResponseStats(aggregatedResponseStats,
-                    packageName, id);
+            responseStatsForUser.populateAllBroadcastResponseStats(
+                    broadcastResponseStatsList, packageName, id);
         }
-        return aggregatedResponseStats;
+        return broadcastResponseStatsList;
     }
 
-    void clearBroadcastResponseStats(int callingUid, @NonNull String packageName, long id,
+    void clearBroadcastResponseStats(int callingUid, @Nullable String packageName, long id,
             @UserIdInt int userId) {
         synchronized (mLock) {
             final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
@@ -210,6 +212,16 @@
         }
     }
 
+    void clearBroadcastEvents(int callingUid, @UserIdInt int userId) {
+        synchronized (mLock) {
+            final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId);
+            if (userBroadcastEvents == null) {
+                return;
+            }
+            userBroadcastEvents.clear(callingUid);
+        }
+    }
+
     void onUserRemoved(@UserIdInt int userId) {
         synchronized (mLock) {
             mUserBroadcastEvents.remove(userId);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4a761a7..06aa8f0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -49,7 +49,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppLaunchEstimateInfo;
 import android.app.usage.AppStandbyInfo;
-import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.EventStats;
 import android.app.usage.IUsageStatsManager;
@@ -2686,16 +2686,15 @@
 
         @Override
         @NonNull
-        public BroadcastResponseStats queryBroadcastResponseStats(
-                @NonNull String packageName,
-                @IntRange(from = 1) long id,
+        public BroadcastResponseStatsList queryBroadcastResponseStats(
+                @Nullable String packageName,
+                @IntRange(from = 0) long id,
                 @NonNull String callingPackage,
                 @UserIdInt int userId) {
-            Objects.requireNonNull(packageName);
             Objects.requireNonNull(callingPackage);
             // TODO: Move to Preconditions utility class
-            if (id <= 0) {
-                throw new IllegalArgumentException("id needs to be >0");
+            if (id < 0) {
+                throw new IllegalArgumentException("id needs to be >=0");
             }
 
             final int callingUid = Binder.getCallingUid();
@@ -2708,8 +2707,9 @@
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
                     userId, false /* allowAll */, false /* requireFull */,
                     "queryBroadcastResponseStats" /* name */, callingPackage);
-            return mResponseStatsTracker.queryBroadcastResponseStats(
-                    callingUid, packageName, id, userId);
+            return new BroadcastResponseStatsList(
+                    mResponseStatsTracker.queryBroadcastResponseStats(
+                            callingUid, packageName, id, userId));
         }
 
         @Override
@@ -2718,10 +2718,9 @@
                 @IntRange(from = 1) long id,
                 @NonNull String callingPackage,
                 @UserIdInt int userId) {
-            Objects.requireNonNull(packageName);
             Objects.requireNonNull(callingPackage);
-            if (id <= 0) {
-                throw new IllegalArgumentException("id needs to be >0");
+            if (id < 0) {
+                throw new IllegalArgumentException("id needs to be >=0");
             }
 
             final int callingUid = Binder.getCallingUid();
@@ -2737,6 +2736,23 @@
             mResponseStatsTracker.clearBroadcastResponseStats(callingUid,
                     packageName, id, userId);
         }
+
+        @Override
+        public void clearBroadcastEvents(@NonNull String callingPackage, @UserIdInt int userId) {
+            Objects.requireNonNull(callingPackage);
+
+            final int callingUid = Binder.getCallingUid();
+            if (!hasPermission(callingPackage)) {
+                throw new SecurityException(
+                        "Caller does not have the permission needed to call this API; "
+                                + "callingPackage=" + callingPackage
+                                + ", callingUid=" + callingUid);
+            }
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+                    userId, false /* allowAll */, false /* requireFull */,
+                    "clearBroadcastResponseStats" /* name */, callingPackage);
+            mResponseStatsTracker.clearBroadcastEvents(callingUid, userId);
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
index 819644846..0ec59c3 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
@@ -51,6 +51,10 @@
     }
 
     void onUidRemoved(int uid) {
+        clear(uid);
+    }
+
+    void clear(int uid) {
         for (int i = mBroadcastEvents.size() - 1; i >= 0; --i) {
             final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
             for (int j = broadcastEvents.size() - 1; j >= 0; --j) {
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
index ac2a320..1828a71 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usage;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.BroadcastResponseStats;
@@ -23,6 +24,8 @@
 
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.util.List;
+
 class UserBroadcastResponseStats {
     /**
      * Contains the mapping of a BroadcastEvent type to it's aggregated stats.
@@ -39,31 +42,38 @@
             BroadcastEvent broadcastEvent) {
         BroadcastResponseStats responseStats = mResponseStats.get(broadcastEvent);
         if (responseStats == null) {
-            responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage());
+            responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage(),
+                    broadcastEvent.getIdForResponseEvent());
             mResponseStats.put(broadcastEvent, responseStats);
         }
         return responseStats;
     }
 
-    void aggregateBroadcastResponseStats(
-            @NonNull BroadcastResponseStats responseStats,
-            @NonNull String packageName, long id) {
+    void populateAllBroadcastResponseStats(
+            @NonNull List<BroadcastResponseStats> broadcastResponseStatsList,
+            @Nullable String packageName, @IntRange(from = 0) long id) {
         for (int i = mResponseStats.size() - 1; i >= 0; --i) {
             final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
-            if (broadcastEvent.getIdForResponseEvent() == id
-                    && broadcastEvent.getTargetPackage().equals(packageName)) {
-                responseStats.addCounts(mResponseStats.valueAt(i));
+            if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+                continue;
             }
+            if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+                continue;
+            }
+            broadcastResponseStatsList.add(mResponseStats.valueAt(i));
         }
     }
 
-    void clearBroadcastResponseStats(@NonNull String packageName, long id) {
+    void clearBroadcastResponseStats(@Nullable String packageName, @IntRange(from = 0) long id) {
         for (int i = mResponseStats.size() - 1; i >= 0; --i) {
             final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
-            if (broadcastEvent.getIdForResponseEvent() == id
-                    && broadcastEvent.getTargetPackage().equals(packageName)) {
-                mResponseStats.removeAt(i);
+            if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+                continue;
             }
+            if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+                continue;
+            }
+            mResponseStats.removeAt(i);
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index fd9b995..42a5af7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,7 +36,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -107,12 +106,6 @@
         return false;
     }
 
-    /**
-     * List of connected MIDI devices
-     */
-    private final HashMap<String, UsbMidiDevice>
-            mMidiDevices = new HashMap<String, UsbMidiDevice>();
-
     // UsbMidiDevice for USB peripheral mode (gadget) device
     private UsbMidiDevice mPeripheralMidiDevice = null;
 
@@ -256,45 +249,6 @@
             }
         }
 
-        // look for MIDI devices
-        boolean hasMidi = parser.hasMIDIInterface();
-        int midiNumInputs = parser.calculateNumLegacyMidiInputs();
-        int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
-        if (DEBUG) {
-            Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
-            Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
-        }
-        if (hasMidi && mHasMidiFeature) {
-            int device = 0;
-            Bundle properties = new Bundle();
-            String manufacturer = usbDevice.getManufacturerName();
-            String product = usbDevice.getProductName();
-            String version = usbDevice.getVersion();
-            String name;
-            if (manufacturer == null || manufacturer.isEmpty()) {
-                name = product;
-            } else if (product == null || product.isEmpty()) {
-                name = manufacturer;
-            } else {
-                name = manufacturer + " " + product;
-            }
-            properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
-            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
-            properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
-            properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
-            properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
-                    usbDevice.getSerialNumber());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
-            properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
-            UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
-                    cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
-            if (usbMidiDevice != null) {
-                mMidiDevices.put(deviceAddress, usbMidiDevice);
-            }
-        }
-
         logDevices("deviceAdded()");
 
         if (DEBUG) {
@@ -315,13 +269,6 @@
             selectDefaultDevice(); // if there any external devices left, select one of them
         }
 
-        // MIDI
-        UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
-        if (usbMidiDevice != null) {
-            Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
-            IoUtils.closeQuietly(usbMidiDevice);
-        }
-
         logDevices("usbDeviceRemoved()");
 
     }
@@ -377,12 +324,6 @@
             usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
         }
 
-        for (String deviceAddr : mMidiDevices.keySet()) {
-            // A UsbMidiDevice does not have a handle to the UsbDevice anymore
-            mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
-                    UsbAlsaManagerProto.MIDI_DEVICES);
-        }
-
         dump.end(token);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 2fb67d7..7f70301 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -498,6 +498,7 @@
 
         // current USB state
         private boolean mHostConnected;
+        private boolean mUsbAccessoryConnected;
         private boolean mSourcePower;
         private boolean mSinkPower;
         private boolean mConfigured;
@@ -964,10 +965,10 @@
                     break;
                 case MSG_UPDATE_HOST_STATE:
                     Iterator devices = (Iterator) msg.obj;
-                    boolean connected = (msg.arg1 == 1);
+                    mUsbAccessoryConnected = (msg.arg1 == 1);
 
                     if (DEBUG) {
-                        Slog.i(TAG, "HOST_STATE connected:" + connected);
+                        Slog.i(TAG, "HOST_STATE connected:" + mUsbAccessoryConnected);
                     }
 
                     mHideUsbNotification = false;
@@ -1221,7 +1222,7 @@
             } else if (mSourcePower) {
                 titleRes = com.android.internal.R.string.usb_supplying_notification_title;
                 id = SystemMessage.NOTE_USB_SUPPLYING;
-            } else if (mHostConnected && mSinkPower && mUsbCharging) {
+            } else if (mHostConnected && mSinkPower && (mUsbCharging || mUsbAccessoryConnected)) {
                 titleRes = com.android.internal.R.string.usb_charging_notification_title;
                 id = SystemMessage.NOTE_USB_CHARGING;
             }
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
similarity index 65%
rename from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
rename to services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 13d404c..0fa79df 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -20,6 +20,7 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
@@ -43,16 +44,20 @@
 import java.util.ArrayList;
 
 /**
- * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ * Opens device connections to MIDI 1.0 or MIDI 2.0 endpoints.
+ * This endpoint will not use ALSA and opens a UsbDeviceConnection directly.
  */
-public final class UsbUniversalMidiDevice implements Closeable {
-    private static final String TAG = "UsbUniversalMidiDevice";
+public final class UsbDirectMidiDevice implements Closeable {
+    private static final String TAG = "UsbDirectMidiDevice";
     private static final boolean DEBUG = false;
 
     private Context mContext;
     private UsbDevice mUsbDevice;
     private UsbDescriptorParser mParser;
     private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+    private final boolean mIsUniversalMidiDevice;
+    private final String mUniqueUsbDeviceIdentifier;
+    private final boolean mShouldCallSetInterface;
 
     // USB outputs are MIDI inputs
     private final InputReceiverProxy[] mMidiInputPortReceivers;
@@ -64,6 +69,11 @@
     // event schedulers for each input port of the physical device
     private MidiEventScheduler[] mEventSchedulers;
 
+    // Arbitrary number for timeout to not continue sending/receiving number from
+    // an inactive device. This number tries to balances the number of cycles and
+    // not being permanently stuck.
+    private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 100;
+
     private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
     private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
     private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
@@ -74,6 +84,8 @@
     private final Object mLock = new Object();
     private boolean mIsOpen;
 
+    private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+
     private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
 
         @Override
@@ -140,13 +152,15 @@
     }
 
     /**
-     * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+     * Creates an UsbDirectMidiDevice based on the input parameters. Read/Write streams
      * will be created individually as some devices don't have the same number of
      * inputs and outputs.
      */
-    public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
-            UsbDescriptorParser parser) {
-        UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+    public static UsbDirectMidiDevice create(Context context, UsbDevice usbDevice,
+            UsbDescriptorParser parser, boolean isUniversalMidiDevice,
+            String uniqueUsbDeviceIdentifier) {
+        UsbDirectMidiDevice midiDevice = new UsbDirectMidiDevice(usbDevice, parser,
+                isUniversalMidiDevice, uniqueUsbDeviceIdentifier);
         if (!midiDevice.register(context)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -155,11 +169,22 @@
         return midiDevice;
     }
 
-    private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+    private UsbDirectMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser,
+            boolean isUniversalMidiDevice, String uniqueUsbDeviceIdentifier) {
         mUsbDevice = usbDevice;
         mParser = parser;
+        mUniqueUsbDeviceIdentifier = uniqueUsbDeviceIdentifier;
+        mIsUniversalMidiDevice = isUniversalMidiDevice;
 
-        mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        // Set interface should only be called when alternate interfaces exist.
+        // Otherwise, USB devices may not handle this gracefully.
+        mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1);
+
+        if (isUniversalMidiDevice) {
+            mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        } else {
+            mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors();
+        }
 
         int numInputs = 0;
         int numOutputs = 0;
@@ -182,8 +207,8 @@
         mNumInputs = numInputs;
         mNumOutputs = numOutputs;
 
-        Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
-                + numOutputs + " outputs");
+        Log.d(TAG, "Created UsbDirectMidiDevice with " + numInputs + " inputs and "
+                + numOutputs + " outputs. isUniversalMidiDevice: " + isUniversalMidiDevice);
 
         // Create MIDI port receivers based on the number of output ports. The
         // output of USB is the input of MIDI.
@@ -218,23 +243,25 @@
             if (doesInterfaceContainInput
                     && doesInterfaceContainOutput) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-
-                // 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).
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
-                        0,
+                        interfaceDescriptor.getInterfaceNumber(),
                         interfaceDescriptor.getAlternateSetting());
+
                 connection.close();
                 return defaultMidiProtocol;
             }
         }
 
-        Log.d(TAG, "Cannot find interface with both input and output endpoints");
+        Log.w(TAG, "Cannot find interface with both input and output endpoints");
         return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
     }
 
     private boolean openLocked() {
+        Log.d(TAG, "openLocked()");
         UsbManager manager = mContext.getSystemService(UsbManager.class);
 
         mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
@@ -258,8 +285,10 @@
             }
             if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-                connection.setInterface(interfaceDescriptor.toAndroid(mParser));
-                connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 mUsbDeviceConnections.add(connection);
                 mInputUsbEndpoints.add(inputEndpoints);
                 mOutputUsbEndpoints.add(outputEndpoints);
@@ -283,14 +312,17 @@
             for (int endpointIndex = 0;
                     endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
+                        mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portFinal = portNumber;
 
-                new Thread("UsbUniversalMidiDevice input thread " + portF) {
+                new Thread("UsbDirectMidiDevice input thread " + portFinal) {
                     @Override
                     public void run() {
-                        byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+                        byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
+                        Log.d(TAG, "input buffer size: " + inputBuffer.length);
                         try {
                             while (true) {
                                 // Record time of event immediately after waking.
@@ -298,20 +330,34 @@
                                 synchronized (mLock) {
                                     if (!mIsOpen) break;
 
-                                    int nRead = connectionF.bulkTransfer(epF, inputBuffer,
-                                            inputBuffer.length, 0);
-
-                                    // For USB, each 32 bit word of a UMP is
-                                    // sent with the least significant byte first.
-                                    swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+                                    int nRead = connectionFinal.bulkTransfer(endpointFinal,
+                                            inputBuffer, inputBuffer.length,
+                                            BULK_TRANSFER_TIMEOUT_MILLISECONDS);
 
                                     if (nRead > 0) {
                                         if (DEBUG) {
-                                            logByteArray("Input ", inputBuffer, 0,
-                                                    nRead);
+                                            logByteArray("Input before conversion ", inputBuffer,
+                                                    0, nRead);
                                         }
-                                        outputReceivers[portF].send(inputBuffer, 0, nRead,
-                                                timestamp);
+                                        byte[] convertedArray;
+                                        if (mIsUniversalMidiDevice) {
+                                            // For USB, each 32 bit word of a UMP is
+                                            // sent with the least significant byte first.
+                                            convertedArray = swapEndiannessPerWord(inputBuffer,
+                                                    nRead);
+                                        } else {
+                                            convertedArray =
+                                                    mUsbMidiPacketConverter.usbMidiToRawMidi(
+                                                             inputBuffer, nRead);
+                                        }
+
+                                        if (DEBUG) {
+                                            logByteArray("Input after conversion ", convertedArray,
+                                                    0, convertedArray.length);
+                                        }
+
+                                        outputReceivers[portFinal].send(convertedArray, 0,
+                                                convertedArray.length, timestamp);
                                     }
                                 }
                             }
@@ -333,19 +379,20 @@
             for (int endpointIndex = 0;
                     endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF =
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
                         mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
-                final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+                final int portFinal = portNumber;
+                final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
 
-                new Thread("UsbUniversalMidiDevice output thread " + portF) {
+                new Thread("UsbDirectMidiDevice output thread " + portFinal) {
                     @Override
                     public void run() {
                         while (true) {
                             MidiEvent event;
                             try {
-                                event = (MidiEvent) eventSchedulerF.waitNextEvent();
+                                event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
                             } catch (InterruptedException e) {
                                 // try again
                                 continue;
@@ -354,16 +401,32 @@
                                 break;
                             }
 
-                            // For USB, each 32 bit word of a UMP is
-                            // sent with the least significant byte first.
-                            swapEndiannessPerWord(event.data, event.count);
-
                             if (DEBUG) {
-                                logByteArray("Output ", event.data, 0,
+                                logByteArray("Output before conversion ", event.data, 0,
                                         event.count);
                             }
-                            connectionF.bulkTransfer(epF, event.data, event.count, 0);
-                            eventSchedulerF.addEventToPool(event);
+
+                            byte[] convertedArray;
+                            if (mIsUniversalMidiDevice) {
+                                // For USB, each 32 bit word of a UMP is
+                                // sent with the least significant byte first.
+                                convertedArray = swapEndiannessPerWord(event.data,
+                                        event.count);
+                            } else {
+                                convertedArray =
+                                        mUsbMidiPacketConverter.rawMidiToUsbMidi(
+                                                 event.data, event.count);
+                            }
+
+                            if (DEBUG) {
+                                logByteArray("Output after conversion ", convertedArray, 0,
+                                        convertedArray.length);
+                            }
+
+                            connectionFinal.bulkTransfer(endpointFinal, convertedArray,
+                                    convertedArray.length,
+                                    BULK_TRANSFER_TIMEOUT_MILLISECONDS);
+                            eventSchedulerFinal.addEventToPool(event);
                         }
                         Log.d(TAG, "output thread exit");
                     }
@@ -381,11 +444,15 @@
         mContext = context;
         MidiManager midiManager = context.getSystemService(MidiManager.class);
         if (midiManager == null) {
-            Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+            Log.e(TAG, "No MidiManager in UsbDirectMidiDevice.create()");
             return false;
         }
 
-        mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        if (mIsUniversalMidiDevice) {
+            mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        } else {
+            mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UNKNOWN;
+        }
 
         Bundle properties = new Bundle();
         String manufacturer = mUsbDevice.getManufacturerName();
@@ -397,8 +464,15 @@
         } else if (product == null || product.isEmpty()) {
             name = manufacturer;
         } else {
-            name = manufacturer + " " + product + " MIDI 2.0";
+            name = manufacturer + " " + product;
         }
+        name += "#" + mUniqueUsbDeviceIdentifier;
+        if (mIsUniversalMidiDevice) {
+            name += " MIDI 2.0";
+        } else {
+            name += " MIDI 1.0";
+        }
+        Log.e(TAG, name);
         properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
         properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
         properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -430,10 +504,13 @@
     }
 
     private void closeLocked() {
+        Log.d(TAG, "closeLocked()");
         for (int i = 0; i < mEventSchedulers.length; i++) {
             mMidiInputPortReceivers[i].setReceiver(null);
             mEventSchedulers[i].close();
         }
+        mEventSchedulers = null;
+
         for (UsbDeviceConnection connection : mUsbDeviceConnections) {
             connection.close();
         }
@@ -444,15 +521,19 @@
         mIsOpen = false;
     }
 
-    private void swapEndiannessPerWord(byte[] array, int size) {
-        for (int i = 0; i + 3 < size; i += 4) {
-            byte tmp = array[i];
-            array[i] = array[i + 3];
-            array[i + 3] = tmp;
-            tmp = array[i + 1];
-            array[i + 1] = array[i + 2];
-            array[i + 2] = tmp;
+    private byte[] swapEndiannessPerWord(byte[] inputArray, int size) {
+        int numberOfExcessBytes = size & 3;
+        if (numberOfExcessBytes != 0) {
+            Log.e(TAG, "size not multiple of 4: " + size);
         }
+        byte[] outputArray = new byte[size - numberOfExcessBytes];
+        for (int i = 0; i + 3 < size; i += 4) {
+            outputArray[i] = inputArray[i + 3];
+            outputArray[i + 1] = inputArray[i + 2];
+            outputArray[i + 2] = inputArray[i + 1];
+            outputArray[i + 3] = inputArray[i];
+        }
+        return outputArray;
     }
 
     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
@@ -465,4 +546,24 @@
         }
         Log.d(TAG, builder.toString());
     }
+
+    private boolean updateUsbInterface(UsbInterface usbInterface,
+            UsbDeviceConnection connection) {
+        if (usbInterface == null) {
+            Log.e(TAG, "Usb Interface is null");
+            return false;
+        }
+        if (!connection.claimInterface(usbInterface, true)) {
+            Log.e(TAG, "Can't claim interface");
+            return false;
+        }
+        if (mShouldCallSetInterface) {
+            if (!connection.setInterface(usbInterface)) {
+                Log.w(TAG, "Can't set interface");
+            }
+        } else {
+            Log.w(TAG, "no alternate interface");
+        }
+        return true;
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 94cc826..a70b0332 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -50,9 +50,12 @@
 import libcore.io.IoUtils;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Random;
 
 /**
  * UsbHostManager manages USB state in host mode.
@@ -94,10 +97,12 @@
     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
 
     /**
-     * List of connected MIDI devices
+     * List of connected MIDI devices. Key on deviceAddress.
      */
-    private final HashMap<String, UsbUniversalMidiDevice>
-            mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+    private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
+            mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
+    private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+    private final Random mRandom = new Random();
     private final boolean mHasMidiFeature;
 
     /*
@@ -423,15 +428,35 @@
                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
                 if (mHasMidiFeature) {
+                    // Use a 3 digit code to associate MIDI devices with one another.
+                    // Each MIDI device already has mId for uniqueness. mId is generated
+                    // sequentially. For clarity, this code is not generated sequentially.
+                    String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier();
+
+                    ArrayList<UsbDirectMidiDevice> midiDevices =
+                            new ArrayList<UsbDirectMidiDevice>();
                     if (parser.containsUniversalMidiDeviceEndpoint()) {
-                        UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
-                                newDevice, parser);
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, true, uniqueUsbDeviceIdentifier);
                         if (midiDevice != null) {
-                            mMidiDevices.put(deviceAddress, midiDevice);
+                            midiDevices.add(midiDevice);
                         } else {
                             Slog.e(TAG, "Universal Midi Device is null.");
                         }
                     }
+                    if (parser.containsLegacyMidiDeviceEndpoint()) {
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, false, uniqueUsbDeviceIdentifier);
+                        if (midiDevice != null) {
+                            midiDevices.add(midiDevice);
+                        } else {
+                            Slog.e(TAG, "Legacy Midi Device is null.");
+                        }
+                    }
+
+                    if (!midiDevices.isEmpty()) {
+                        mMidiDevices.put(deviceAddress, midiDevices);
+                    }
                 }
 
                 // Tracking
@@ -469,10 +494,13 @@
                 mPermissionManager.usbDeviceRemoved(device);
 
                 // MIDI
-                UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
-                if (midiDevice != null) {
-                    Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
-                    IoUtils.closeQuietly(midiDevice);
+                ArrayList<UsbDirectMidiDevice> midiDevices =
+                        mMidiDevices.remove(deviceAddress);
+                for (UsbDirectMidiDevice midiDevice : midiDevices) {
+                    if (midiDevice != null) {
+                        Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+                        IoUtils.closeQuietly(midiDevice);
+                    }
                 }
 
                 getCurrentUserSettings().usbDeviceRemoved(device);
@@ -606,6 +634,19 @@
         return true;
     }
 
+    // Generate a 3 digit code.
+    private String generateNewUsbDeviceIdentifier() {
+        String code;
+        do {
+            code = "";
+            for (int i = 0; i < 3; i++) {
+                code += mRandom.nextInt(10);
+            }
+        } while (mMidiUniqueCodes.contains(code));
+        mMidiUniqueCodes.add(code);
+        return code;
+    }
+
     private native void monitorUsbHostBus();
     private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
 }
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
new file mode 100644
index 0000000..7c93c76
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -0,0 +1,237 @@
+/*
+ * 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.usb;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Converts between MIDI packets and USB MIDI 1.0 packets.
+ */
+public class UsbMidiPacketConverter {
+
+    // Refer to Table 4-1 in USB MIDI 1.0 spec.
+    private static final int[] PAYLOAD_SIZE = new int[]{
+            /* 0x00 */ -1, // Miscellaneous function codes. Reserved for future extensions.
+            /* 0x01 */ -1, // Cable events. Reserved for future expansion.
+            /* 0x02 */  2, // Two-byte System Common messages like MTC, SongSelect, etc
+            /* 0x03 */  3, // Three-byte System Common messages like SPP, etc.
+            /* 0x04 */  3, // SysEx starts or continues
+            /* 0x05 */  1, // Single-byte System Common Message or single-byte SysEx ends.
+            /* 0x06 */  2, // SysEx ends with following two bytes.
+            /* 0x07 */  3, // SysEx ends with following three bytes.
+            /* 0x08 */  3, // Note-off
+            /* 0x09 */  3, // Note-on
+            /* 0x0a */  3, // Poly-KeyPress
+            /* 0x0b */  3, // Control Change
+            /* 0x0c */  2, // Program Change
+            /* 0x0d */  2, // Channel Pressure
+            /* 0x0e */  3, // PitchBend Change
+            /* 0x0f */  1  // Single Byte
+    };
+
+    // Each System MIDI message is a certain size. These can be mapped to a
+    // Code Index number defined in Table 4-1 of USB MIDI 1.0.
+    private static final int[] CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE = new int[]{
+            /* 0x00 */ -1, // Start of Exclusive. Special case.
+            /* 0x01 */  2, // MIDI Time Code. Two byte message
+            /* 0x02 */  3, // Song Point Pointer. Three byte message
+            /* 0x03 */  2, // Song Select. Two byte message
+            /* 0x04 */ -1, // Undefined MIDI System Common
+            /* 0x05 */ -1, // Undefined MIDI System Common
+            /* 0x06 */  5, // Tune Request. One byte message
+            /* 0x07 */ -1, // End of Exclusive. Special case.
+            /* 0x08 */  5, // Timing clock. One byte message
+            /* 0x09 */ -1, // Undefined MIDI System Real-time
+            /* 0x0a */  5, // Start. One byte message
+            /* 0x0b */  5, // Continue. One byte message
+            /* 0x0c */  5, // Stop. One byte message
+            /* 0x0d */ -1, // Undefined MIDI System Real-time
+            /* 0x0e */  5, // Active Sensing. One byte message
+            /* 0x0f */  5  // System Reset. One byte message
+    };
+
+    // These code index numbers also come from Table 4-1 in USB MIDI 1.0 spec.
+    private static final byte CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES = 0x4;
+    private static final byte CODE_INDEX_NUMBER_SINGLE_BYTE = 0xF;
+    private static final byte CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE = (byte) 0x5;
+
+    // System messages are defined in MIDI.
+    private static final byte FIRST_SYSTEM_MESSAGE_VALUE = (byte) 0xF0;
+    private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
+    private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
+
+    private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
+    private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+
+    /**
+     * Converts a USB MIDI array into a raw MIDI array.
+     *
+     * @param usbMidiBytes the USB MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of raw MIDI packets
+     */
+    public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
+        return mUsbMidiDecoder.decode(usbMidiBytes, size);
+    }
+
+    /**
+     * Converts a raw MIDI array into a USB MIDI array.
+     *
+     * @param midiBytes the raw MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of USB MIDI packets
+     */
+    public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
+        return mUsbMidiEncoder.encode(midiBytes, size);
+    }
+
+    private class UsbMidiDecoder {
+        // Decodes the data from USB MIDI to raw MIDI.
+        // Each valid 4 byte input maps to a 1-3 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        public byte[] decode(byte[] usbMidiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            for (int i = 0; i + 3 < size; i += 4) {
+                int codeIndex = usbMidiBytes[i] & 0x0f;
+                int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
+                if (numPayloadBytes < 0) {
+                    continue;
+                }
+                outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    private class UsbMidiEncoder {
+        // In order to facilitate large scale transfers, SysEx can be sent in multiple packets.
+        // If encode() is called without an SysEx end, we must continue SysEx for the next packet.
+        // All other packets should be 3 bytes or less and must be not be broken between packets.
+        private byte[] mStoredSystemExclusiveBytes = new byte[3];
+        private int mNumStoredSystemExclusiveBytes = 0;
+        private boolean mHasSystemExclusiveStarted = false;
+
+        private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+
+        // Encodes the data from raw MIDI to USB MIDI.
+        // Each valid 1-3 byte input maps to a 4 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        // MidiFramer is not needed here as this code handles partial packets.
+        // Long SysEx messages split between packets will encode and return a
+        // byte stream even if the SysEx end has not been sent.
+        // If there are less than 3 remaining data bytes in a SysEx message left,
+        // these bytes will be combined with the next set of packets.
+        public byte[] encode(byte[] midiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            int curLocation = 0;
+            while (curLocation < size) {
+                if (midiBytes[curLocation] >= 0) { // Data byte
+                    if (mHasSystemExclusiveStarted) {
+                        mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                                midiBytes[curLocation];
+                        mNumStoredSystemExclusiveBytes++;
+                        if (mNumStoredSystemExclusiveBytes == 3) {
+                            outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+                            outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
+                            mNumStoredSystemExclusiveBytes = 0;
+                        }
+                    } else {
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                    }
+                    curLocation++;
+                    continue;
+                } else if (midiBytes[curLocation] != SYSEX_END_EXCLUSIVE) {
+                    // SysEx operation was interrupted. Pass the data directly down.
+                    if (mHasSystemExclusiveStarted) {
+                        int index = 0;
+                        while (index < mNumStoredSystemExclusiveBytes) {
+                            writeSingleByte(outputStream, mStoredSystemExclusiveBytes[index]);
+                            index++;
+                        }
+                        mNumStoredSystemExclusiveBytes = 0;
+                        mHasSystemExclusiveStarted = false;
+                    }
+                }
+
+                if (midiBytes[curLocation] < FIRST_SYSTEM_MESSAGE_VALUE) { // Channel message
+                    byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
+                    int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                    if (curLocation + channelMessageSize <= size) {
+                        outputStream.write(codeIndexNumber);
+                        outputStream.write(midiBytes, curLocation, channelMessageSize);
+                        // Fill in the rest of the bytes with 0.
+                        outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
+                        curLocation += channelMessageSize;
+                    } else { // The packet is missing data. Use single byte messages.
+                        while (curLocation < size) {
+                            writeSingleByte(outputStream, midiBytes[curLocation]);
+                            curLocation++;
+                        }
+                    }
+                } else if (midiBytes[curLocation] == SYSEX_START_EXCLUSIVE) {
+                    mHasSystemExclusiveStarted = true;
+                    mStoredSystemExclusiveBytes[0] = midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes = 1;
+                    curLocation++;
+                } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
+                    // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
+                    outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+                            + mNumStoredSystemExclusiveBytes);
+                    mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                            midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes++;
+                    outputStream.write(mStoredSystemExclusiveBytes, 0,
+                             mNumStoredSystemExclusiveBytes);
+                    // Fill in the rest of the bytes with 0.
+                    outputStream.write(mEmptyBytes, 0, 3 - mNumStoredSystemExclusiveBytes);
+                    mHasSystemExclusiveStarted = false;
+                    mNumStoredSystemExclusiveBytes = 0;
+                    curLocation++;
+                } else {
+                    int systemType = midiBytes[curLocation] & 0x0f;
+                    int codeIndexNumber = CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE[systemType];
+                    if (codeIndexNumber < 0) { // Unknown type. Use single byte messages.
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                        curLocation++;
+                    } else {
+                        int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                        if (curLocation + systemMessageSize <= size) {
+                            outputStream.write(codeIndexNumber);
+                            outputStream.write(midiBytes, curLocation, systemMessageSize);
+                            // Fill in the rest of the bytes with 0.
+                            outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
+                            curLocation += systemMessageSize;
+                        } else { // The packet is missing data. Use single byte messages.
+                            while (curLocation < size) {
+                                writeSingleByte(outputStream, midiBytes[curLocation]);
+                                curLocation++;
+                            }
+                        }
+                    }
+                }
+            }
+            return outputStream.toByteArray();
+        }
+
+        private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
+            outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+            outputStream.write(byteToWrite);
+            outputStream.write(0);
+            outputStream.write(0);
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index d0825ba..f07a406 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -384,7 +384,7 @@
     }
 
     /**
-     * Enables USB data when disabled due to {@link UsbPortStatus#USB_DATA_STATUS_DISABLED_DOCK}
+     * Enables USB data when disabled due to {@link UsbPortStatus#DATA_STATUS_DISABLED_DOCK}
      */
     public void enableUsbDataWhileDocked(@NonNull String portId, long transactionId,
             IUsbOperationInternal callback, IndentingPrintWriter pw) {
@@ -844,7 +844,7 @@
                         portInfo.contaminantDetectionStatus,
                         portInfo.usbDataStatus,
                         portInfo.powerTransferLimited,
-                        portInfo.powerBrickStatus, pw);
+                        portInfo.powerBrickConnectionStatus, pw);
             }
         } else {
             for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -859,7 +859,7 @@
                         currentPortInfo.contaminantDetectionStatus,
                         currentPortInfo.usbDataStatus,
                         currentPortInfo.powerTransferLimited,
-                        currentPortInfo.powerBrickStatus, pw);
+                        currentPortInfo.powerBrickConnectionStatus, pw);
             }
         }
 
@@ -895,9 +895,9 @@
             int contaminantProtectionStatus,
             boolean supportsEnableContaminantPresenceDetection,
             int contaminantDetectionStatus,
-            int[] usbDataStatus,
+            int usbDataStatus,
             boolean powerTransferLimited,
-            int powerBrickStatus,
+            int powerBrickConnectionStatus,
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
@@ -957,7 +957,7 @@
                     currentDataRole, canChangeDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
                     contaminantDetectionStatus, usbDataStatus,
-                    powerTransferLimited, powerBrickStatus);
+                    powerTransferLimited, powerBrickConnectionStatus);
             mPorts.put(portId, portInfo);
         } else {
             // Validate that ports aren't changing definition out from under us.
@@ -995,7 +995,7 @@
                     currentDataRole, canChangeDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
                     contaminantDetectionStatus, usbDataStatus,
-                    powerTransferLimited, powerBrickStatus)) {
+                    powerTransferLimited, powerBrickConnectionStatus)) {
                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
             } else {
                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1222,7 +1222,7 @@
                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                         supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
                         UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED,
-                        new int[]{UsbPortStatus.USB_DATA_STATUS_UNKNOWN}, false,
+                        UsbPortStatus.DATA_STATUS_UNKNOWN, false,
                         UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN);
                 dispositionChanged = true;
             }
@@ -1238,31 +1238,12 @@
             return dispositionChanged;
         }
 
-        private boolean dataStatusEquals(int[] dataStatusL, int[] dataStatusR) {
-            if (dataStatusL == null && dataStatusR == null) {
-                return true;
-            }
-            if ((dataStatusL == null && dataStatusR != null)
-                || (dataStatusL != null && dataStatusR == null)) {
-                return false;
-            }
-            if (dataStatusL.length != dataStatusR.length) {
-                return false;
-            }
-            for (int i = 0; i < dataStatusL.length; i++) {
-                if (dataStatusL[i] != dataStatusR[i]) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
         public boolean setStatus(int currentMode, boolean canChangeMode,
                 int currentPowerRole, boolean canChangePowerRole,
                 int currentDataRole, boolean canChangeDataRole,
                 int supportedRoleCombinations, int contaminantProtectionStatus,
-                int contaminantDetectionStatus, int[] usbDataStatus,
-                boolean powerTransferLimited, int powerBrickStatus) {
+                int contaminantDetectionStatus, int usbDataStatus,
+                boolean powerTransferLimited, int powerBrickConnectionStatus) {
             boolean dispositionChanged = false;
 
             mCanChangeMode = canChangeMode;
@@ -1278,15 +1259,16 @@
                     != contaminantProtectionStatus
                     || mUsbPortStatus.getContaminantDetectionStatus()
                     != contaminantDetectionStatus
-                    || !dataStatusEquals(mUsbPortStatus.getUsbDataStatus(), usbDataStatus)
+                    || mUsbPortStatus.getUsbDataStatus()
+                    != usbDataStatus
                     || mUsbPortStatus.isPowerTransferLimited()
                     != powerTransferLimited
-                    || mUsbPortStatus.getPowerBrickStatus()
-                    != powerBrickStatus) {
+                    || mUsbPortStatus.getPowerBrickConnectionStatus()
+                    != powerBrickConnectionStatus) {
                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
                         supportedRoleCombinations, contaminantProtectionStatus,
                         contaminantDetectionStatus, usbDataStatus,
-                        powerTransferLimited, powerBrickStatus);
+                        powerTransferLimited, powerBrickConnectionStatus);
                 dispositionChanged = true;
             }
 
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index f3308bb..c0ecf58 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -63,6 +63,8 @@
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -883,6 +885,7 @@
         }
     }
 
+    @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6e68a91..cd6ea68 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -198,14 +198,20 @@
                 if (mCurInterfaceDescriptor != null) {
                     switch (mCurInterfaceDescriptor.getUsbClass()) {
                         case UsbDescriptor.CLASSID_AUDIO:
-                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbACInterface.allocDescriptor(this, stream, length, type);
+                            if (descriptor instanceof UsbMSMidiHeader) {
+                                mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor(
+                                        descriptor);
+                            }
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO:
                             if (DEBUG) {
                                 Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO");
                             }
-                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbVCInterface.allocDescriptor(this, stream, length, type);
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
@@ -218,7 +224,6 @@
                             Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
-                    mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
                 }
                 break;
 
@@ -674,6 +679,23 @@
     public boolean containsUniversalMidiDeviceEndpoint() {
         ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
                 findUniversalMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean containsLegacyMidiDeviceEndpoint() {
+        ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+                findLegacyMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean doesInterfaceContainEndpoint(
+            ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) {
         int outputCount = 0;
         int inputCount = 0;
         for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
@@ -698,10 +720,24 @@
      * @hide
      */
     public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_2_0);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_1_0);
+    }
+
+    /**
+     * @hide
+     */
+    private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
-        ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+        ArrayList<UsbInterfaceDescriptor> midiInterfaces =
                 new ArrayList<UsbInterfaceDescriptor>();
 
         for (UsbDescriptor descriptor : descriptors) {
@@ -709,14 +745,14 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
-                                universalMidiInterfaces.add(interfaceDescriptor);
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == type) {
+                                midiInterfaces.add(interfaceDescriptor);
                             }
                         }
                     }
@@ -726,10 +762,13 @@
                         + " t:0x" + Integer.toHexString(descriptor.getType()));
             }
         }
-        return universalMidiInterfaces;
+        return midiInterfaces;
     }
 
-    private int calculateNumLegacyMidiPorts(boolean isOutput) {
+    /**
+     * @hide
+     */
+    public int calculateMidiInterfaceDescriptorsCount() {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -738,22 +777,12 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
-                                continue;
-                            }
-                        }
-                    }
-                    for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
-                        UsbEndpointDescriptor endpoint =
-                                interfaceDescriptor.getEndpointDescriptor(i);
-                        // 0 is output, 1 << 7 is input.
-                        if ((endpoint.getDirection() == 0) == isOutput) {
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
                             count++;
                         }
                     }
@@ -769,20 +798,6 @@
     /**
      * @hide
      */
-    public int calculateNumLegacyMidiInputs() {
-        return calculateNumLegacyMidiPorts(false /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
-    public int calculateNumLegacyMidiOutputs() {
-        return calculateNumLegacyMidiPorts(true /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
     public float getInputHeadsetProbability() {
         if (hasMIDIInterface()) {
             return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index ca4613b..9ddcb10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,7 +42,8 @@
     private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
             new ArrayList<UsbEndpointDescriptor>();
 
-    private UsbDescriptor mClassSpecificInterfaceDescriptor;
+    // Used for MIDI only.
+    private UsbDescriptor mMidiHeaderInterfaceDescriptor;
 
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
@@ -107,12 +108,12 @@
         mEndpointDescriptors.add(endpoint);
     }
 
-    public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
-        mClassSpecificInterfaceDescriptor = descriptor;
+    public void setMidiHeaderInterfaceDescriptor(UsbDescriptor descriptor) {
+        mMidiHeaderInterfaceDescriptor = descriptor;
     }
 
-    public UsbDescriptor getClassSpecificInterfaceDescriptor() {
-        return mClassSpecificInterfaceDescriptor;
+    public UsbDescriptor getMidiHeaderInterfaceDescriptor() {
+        return mMidiHeaderInterfaceDescriptor;
     }
 
     /**
diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
index dd25620..128a051 100644
--- a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
+++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
@@ -37,9 +37,9 @@
     public int contaminantProtectionStatus;
     public boolean supportsEnableContaminantPresenceDetection;
     public int contaminantDetectionStatus;
-    public int[] usbDataStatus;
+    public int usbDataStatus;
     public boolean powerTransferLimited;
-    public int powerBrickStatus;
+    public int powerBrickConnectionStatus;
 
     public RawPortInfo(String portId, int supportedModes) {
         this.portId = portId;
@@ -49,9 +49,10 @@
         this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
         this.supportsEnableContaminantPresenceDetection = false;
         this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
-        this.usbDataStatus[0] = UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+        this.usbDataStatus = UsbPortStatus.DATA_STATUS_UNKNOWN;
+
         this.powerTransferLimited = false;
-        this.powerBrickStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+        this.powerBrickConnectionStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
     }
 
     public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
@@ -62,9 +63,9 @@
             int contaminantProtectionStatus,
             boolean supportsEnableContaminantPresenceDetection,
             int contaminantDetectionStatus,
-            int[] usbDataStatus,
+            int usbDataStatus,
             boolean powerTransferLimited,
-            int powerBrickStatus) {
+            int powerBrickConnectionStatus) {
         this.portId = portId;
         this.supportedModes = supportedModes;
         this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
@@ -82,7 +83,7 @@
         this.contaminantDetectionStatus = contaminantDetectionStatus;
         this.usbDataStatus = usbDataStatus;
         this.powerTransferLimited = powerTransferLimited;
-        this.powerBrickStatus = powerBrickStatus;
+        this.powerBrickConnectionStatus = powerBrickConnectionStatus;
     }
 
     @Override
@@ -105,10 +106,9 @@
         dest.writeInt(contaminantProtectionStatus);
         dest.writeBoolean(supportsEnableContaminantPresenceDetection);
         dest.writeInt(contaminantDetectionStatus);
-        dest.writeInt(usbDataStatus.length);
-        dest.writeIntArray(usbDataStatus);
+        dest.writeInt(usbDataStatus);
         dest.writeBoolean(powerTransferLimited);
-        dest.writeInt(powerBrickStatus);
+        dest.writeInt(powerBrickConnectionStatus);
     }
 
     public static final Parcelable.Creator<RawPortInfo> CREATOR =
@@ -128,10 +128,9 @@
             int contaminantProtectionStatus = in.readInt();
             boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
             int contaminantDetectionStatus = in.readInt();
-            int[] usbDataStatus = new int[in.readInt()];
-            in.readIntArray(usbDataStatus);
+            int usbDataStatus = in.readInt();
             boolean powerTransferLimited = in.readBoolean();
-            int powerBrickStatus = in.readInt();
+            int powerBrickConnectionStatus = in.readInt();
             return new RawPortInfo(id, supportedModes,
                     supportedContaminantProtectionModes, currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
@@ -140,7 +139,7 @@
                     contaminantProtectionStatus,
                     supportsEnableContaminantPresenceDetection,
                     contaminantDetectionStatus, usbDataStatus,
-                    powerTransferLimited, powerBrickStatus);
+                    powerTransferLimited, powerBrickConnectionStatus);
         }
 
         @Override
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index f468db3..1db018e 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -73,6 +73,42 @@
     private boolean mSystemReady;
     private long mTransactionId;
 
+    /**
+     * USB data status is not known.
+     */
+    public static final int USB_DATA_STATUS_UNKNOWN = 0;
+
+    /**
+     * USB data is enabled.
+     */
+    public static final int USB_DATA_STATUS_ENABLED = 1;
+
+    /**
+     * USB data is disabled as the port is too hot.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
+
+    /**
+     * USB data is disabled due to contaminated port.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
+
+    /**
+     * USB data is disabled due to docking event.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
+
+    /**
+     * USB data is disabled by
+     * {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
+
+    /**
+     * USB data is disabled for debug.
+     */
+    public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
+
     public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
         synchronized (mLock) {
             if (mProxy == null) {
@@ -489,12 +525,34 @@
             return supportedContaminantProtectionModes;
         }
 
-        private int[] toIntArray(byte[] input) {
-            int[] output = new int[input.length];
-            for (int i = 0; i < input.length; i++) {
-                output[i] = input[i];
+        private int toUsbDataStatusInt(byte[] usbDataStatusHal) {
+            int usbDataStatus = UsbPortStatus.DATA_STATUS_UNKNOWN;
+            for (int i = 0; i < usbDataStatusHal.length; i++) {
+                switch (usbDataStatusHal[i]) {
+                    case USB_DATA_STATUS_ENABLED:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_ENABLED;
+                        break;
+                    case USB_DATA_STATUS_DISABLED_OVERHEAT:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_OVERHEAT;
+                        break;
+                    case USB_DATA_STATUS_DISABLED_CONTAMINANT:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_CONTAMINANT;
+                        break;
+                    case USB_DATA_STATUS_DISABLED_DOCK:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
+                        break;
+                    case USB_DATA_STATUS_DISABLED_FORCE:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
+                        break;
+                    case USB_DATA_STATUS_DISABLED_DEBUG:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DEBUG;
+                        break;
+                    default:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_UNKNOWN;
+                }
             }
-            return output;
+            UsbPortManager.logAndPrint(Log.INFO, mPw, "AIDL UsbDataStatus:" + usbDataStatus);
+            return usbDataStatus;
         }
 
         @Override
@@ -528,7 +586,7 @@
                         toContaminantProtectionStatus(current.contaminantProtectionStatus),
                         current.supportsEnableContaminantPresenceDetection,
                         current.contaminantDetectionStatus,
-                        toIntArray(current.usbDataStatus),
+                        toUsbDataStatusInt(current.usbDataStatus),
                         current.powerTransferLimited,
                         current.powerBrickStatus);
                 newPortInfo.add(temp);
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
index 64e8adc..c7f0775 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
@@ -33,8 +33,8 @@
 import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
-import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_UNKNOWN;
 
 
 import static com.android.server.usb.UsbPortManager.logAndPrint;
@@ -85,7 +85,7 @@
     private HALCallback mHALCallback;
     private boolean mSystemReady;
     // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
-    private static int sUsbDataStatus = USB_DATA_STATUS_UNKNOWN;
+    private static int sUsbDataStatus = DATA_STATUS_UNKNOWN;
 
     public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
         int version;
@@ -375,7 +375,7 @@
             }
         }
         if (success) {
-            sUsbDataStatus = enable ? USB_DATA_STATUS_UNKNOWN : USB_DATA_STATUS_DISABLED_FORCE;
+            sUsbDataStatus = enable ? DATA_STATUS_UNKNOWN : DATA_STATUS_DISABLED_FORCE;
         }
         try {
             callback.onOperationComplete(success
@@ -421,7 +421,7 @@
                         current.canChangePowerRole,
                         current.currentDataRole, current.canChangeDataRole,
                         false, CONTAMINANT_PROTECTION_NONE,
-                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataStatus,
                         false, POWER_BRICK_STATUS_UNKNOWN);
                 newPortInfo.add(temp);
                 UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
@@ -455,7 +455,7 @@
                         current.status.canChangePowerRole,
                         current.status.currentDataRole, current.status.canChangeDataRole,
                         false, CONTAMINANT_PROTECTION_NONE,
-                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataStatus,
                         false, POWER_BRICK_STATUS_UNKNOWN);
                 newPortInfo.add(temp);
                 UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
@@ -493,7 +493,7 @@
                         current.contaminantProtectionStatus,
                         current.supportsEnableContaminantPresenceDetection,
                         current.contaminantDetectionStatus,
-                        new int[sUsbDataStatus],
+                        sUsbDataStatus,
                         false, POWER_BRICK_STATUS_UNKNOWN);
                 newPortInfo.add(temp);
                 UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f43e5aa..1d06707 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1179,7 +1179,7 @@
         if (service != null) {
             try {
                 return service.getSimCallManager(
-                        SubscriptionManager.getDefaultSubscriptionId());
+                        SubscriptionManager.getDefaultSubscriptionId(), mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
             }
@@ -1201,7 +1201,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSimCallManager(subscriptionId);
+                return service.getSimCallManager(subscriptionId, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManager");
             }
@@ -1225,7 +1225,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSimCallManagerForUser(userId);
+                return service.getSimCallManagerForUser(userId, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getSimCallManagerForUser");
             }
@@ -1481,7 +1481,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.registerPhoneAccount(account);
+                service.registerPhoneAccount(account, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#registerPhoneAccount", e);
             }
@@ -1497,7 +1497,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.unregisterPhoneAccount(accountHandle);
+                service.unregisterPhoneAccount(accountHandle, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#unregisterPhoneAccount", e);
             }
@@ -1578,7 +1578,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getDefaultDialerPackage();
+                return service.getDefaultDialerPackage(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
             }
@@ -1652,7 +1652,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getSystemDialerPackage();
+                return service.getSystemDialerPackage(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
             }
@@ -2057,7 +2057,8 @@
                             "acceptHandover for API > O-MR1");
                     return;
                 }
-                service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras);
+                service.addNewIncomingCall(phoneAccount, extras == null ? new Bundle() : extras,
+                        mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
             }
@@ -2099,7 +2100,8 @@
         if (service != null) {
             try {
                 service.addNewIncomingConference(
-                        phoneAccount, extras == null ? new Bundle() : extras);
+                        phoneAccount, extras == null ? new Bundle() : extras,
+                        mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
             }
@@ -2395,7 +2397,7 @@
         Intent result = null;
         if (service != null) {
             try {
-                result = service.createManageBlockedNumbersIntent();
+                result = service.createManageBlockedNumbersIntent(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#createManageBlockedNumbersIntent", e);
             }
@@ -2552,7 +2554,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                service.acceptHandover(srcAddr, videoState, destAcct);
+                service.acceptHandover(srcAddr, videoState, destAcct, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException acceptHandover: " + e);
             }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b9936ce..151f742 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -99,22 +99,22 @@
     /**
      * @see TelecomServiceImpl#getSimCallManager
      */
-    PhoneAccountHandle getSimCallManager(int subId);
+    PhoneAccountHandle getSimCallManager(int subId, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#getSimCallManagerForUser
      */
-    PhoneAccountHandle getSimCallManagerForUser(int userId);
+    PhoneAccountHandle getSimCallManagerForUser(int userId, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#registerPhoneAccount
      */
-    void registerPhoneAccount(in PhoneAccount metadata);
+    void registerPhoneAccount(in PhoneAccount metadata, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#unregisterPhoneAccount
      */
-    void unregisterPhoneAccount(in PhoneAccountHandle account);
+    void unregisterPhoneAccount(in PhoneAccountHandle account, String callingPackage);
 
     /**
      * @see TelecomServiceImpl#clearAccounts
@@ -147,7 +147,7 @@
     /**
      * @see TelecomServiceImpl#getDefaultDialerPackage
      */
-    String getDefaultDialerPackage();
+    String getDefaultDialerPackage(String callingPackage);
 
     /**
      * @see TelecomServiceImpl#getDefaultDialerPackage
@@ -157,7 +157,7 @@
     /**
      * @see TelecomServiceImpl#getSystemDialerPackage
      */
-    String getSystemDialerPackage();
+    String getSystemDialerPackage(String callingPackage);
 
     /**
     * @see TelecomServiceImpl#dumpCallAnalytics
@@ -255,12 +255,15 @@
     /**
      * @see TelecomServiceImpl#addNewIncomingCall
      */
-    void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+    void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras,
+            String callingPackage);
 
     /**
      * @see TelecomServiceImpl#addNewIncomingConference
      */
-    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+    void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras,
+            String callingPackage);
+
 
     /**
      * @see TelecomServiceImpl#addNewUnknownCall
@@ -296,7 +299,7 @@
     /**
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
-    Intent createManageBlockedNumbersIntent();
+    Intent createManageBlockedNumbersIntent(String callingPackage);
 
    /**
     * @see TelecomServiceImpl#createLaunchEmergencyDialerIntent
@@ -323,7 +326,8 @@
     /**
      * @see TelecomServiceImpl#acceptHandover
      */
-    void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
+    void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct,
+                String callingPackage);
 
     /**
      * @see TelecomServiceImpl#setTestEmergencyPhoneAccountPackageNameFilter
diff --git a/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.aidl b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.aidl
new file mode 100644
index 0000000..542c6ad
--- /dev/null
+++ b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable ActivityStatsTechSpecificInfo;
diff --git a/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java
new file mode 100644
index 0000000..e5a20ea
--- /dev/null
+++ b/telephony/java/android/telephony/ActivityStatsTechSpecificInfo.java
@@ -0,0 +1,245 @@
+/*
+ * 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.telephony;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ServiceState.FrequencyRange;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Technology specific activity stats info. List of the activity stats for each RATs (2G, 3G, 4G and
+ * 5G) and frequency ranges (HIGH for sub6 and MMWAVE) in case of 5G. In case implementation doesn't
+ * have RAT specific activity stats then send only one activity stats info with RAT unknown.
+ *
+ * @hide
+ */
+public final class ActivityStatsTechSpecificInfo implements Parcelable {
+    private static final int TX_POWER_LEVELS = 5;
+
+    private int mRat;
+    private int mFrequencyRange;
+    private int[] mTxTimeMs;
+    private int mRxTimeMs;
+
+    /** @hide */
+    public ActivityStatsTechSpecificInfo(
+            int rat, @FrequencyRange int frequencyRange, @NonNull int[] txTimeMs, int rxTimeMs) {
+        Objects.requireNonNull(txTimeMs);
+        if (txTimeMs.length != TX_POWER_LEVELS) {
+            throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+        }
+        mRat = rat;
+        mFrequencyRange = frequencyRange;
+        mTxTimeMs = txTimeMs;
+        mRxTimeMs = rxTimeMs;
+    }
+
+    /**
+     * Returns the radio access technology for this activity stats info.
+     *
+     * The returned value is define in {@link AccessNetworkConstants.AccessNetworkType};
+     * @hide
+     */
+    public int getRat() {
+        return mRat;
+    }
+
+    /**
+     * Returns the rough frequency range for this activity stats info.
+     *
+     * The returned value is define in {@link ServiceState.FrequencyRange};
+     * @hide
+     */
+    public @FrequencyRange int getFrequencyRange() {
+        return mFrequencyRange;
+    }
+
+    /**
+     * Gets the amount of time the modem spent transmitting at a certain power level.
+     *
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the given
+     *     power level.
+     */
+    public @DurationMillisLong long getTransmitTimeMillis(int powerLevel) {
+        return mTxTimeMs[powerLevel];
+    }
+
+    /**
+     * @return The raw array of transmit power durations
+     * @hide
+     */
+    @NonNull
+    public int[] getTransmitTimeMillis() {
+        return mTxTimeMs;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+     *
+     * @return Time in milliseconds.
+     * @hide
+     */
+    public @DurationMillisLong long getReceiveTimeMillis() {
+        return mRxTimeMs;
+    }
+    /** @hide */
+    public void setRat(int rat) {
+        mRat = rat;
+    }
+
+    /** @hide */
+    public void setFrequencyRange(@FrequencyRange int frequencyRange) {
+        mFrequencyRange = frequencyRange;
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int receiveTimeMillis) {
+        mRxTimeMs = receiveTimeMillis;
+    }
+
+    /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     *
+     * @hide
+     */
+    public void setReceiveTimeMillis(long receiveTimeMillis) {
+        mRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /** @hide */
+    public void setTransmitTimeMillis(int[] txTimeMs) {
+        mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
+    }
+
+    /** @hide */
+    public boolean isTxPowerValid() {
+        return Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
+    }
+
+    /** @hide */
+    public boolean isRxPowerValid() {
+        return getReceiveTimeMillis() >= 0;
+    }
+
+    /** @hide */
+    public boolean isTxPowerEmpty() {
+        boolean isTxPowerEmpty =
+                mTxTimeMs == null
+                        || mTxTimeMs.length == 0
+                        || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
+        return isTxPowerEmpty;
+    }
+
+    /** @hide */
+    public boolean isRxPowerEmpty() {
+        return getReceiveTimeMillis() == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mRat, mFrequencyRange, mRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTxTimeMs);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ActivityStatsTechSpecificInfo)) return false;
+        ActivityStatsTechSpecificInfo that = (ActivityStatsTechSpecificInfo) o;
+        return mRat == that.mRat
+                && mFrequencyRange == that.mFrequencyRange
+                && Arrays.equals(mTxTimeMs, that.mTxTimeMs)
+                && mRxTimeMs == that.mRxTimeMs;
+    }
+
+    private static String ratToString(int type) {
+        switch (type) {
+            case AccessNetworkConstants.AccessNetworkType.UNKNOWN:
+                return "UNKNOWN";
+            case AccessNetworkConstants.AccessNetworkType.GERAN:
+                return "GERAN";
+            case AccessNetworkConstants.AccessNetworkType.UTRAN:
+                return "UTRAN";
+            case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+                return "EUTRAN";
+            case AccessNetworkConstants.AccessNetworkType.CDMA2000:
+                return "CDMA2000";
+            case AccessNetworkConstants.AccessNetworkType.IWLAN:
+                return "IWLAN";
+            case AccessNetworkConstants.AccessNetworkType.NGRAN:
+                return "NGRAN";
+            default:
+                return Integer.toString(type);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("{mRat=")
+                .append(ratToString(mRat))
+                .append(",mFrequencyRange=")
+                .append(ServiceState.frequencyRangeToString(mFrequencyRange))
+                .append(",mTxTimeMs[]=")
+                .append(Arrays.toString(mTxTimeMs))
+                .append(",mRxTimeMs=")
+                .append(mRxTimeMs)
+                .append("}")
+                .toString();
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<
+                    ActivityStatsTechSpecificInfo>
+            CREATOR =
+                    new Parcelable.Creator<ActivityStatsTechSpecificInfo>() {
+                public ActivityStatsTechSpecificInfo createFromParcel(@NonNull Parcel in) {
+                    int rat = in.readInt();
+                    int frequencyRange = in.readInt();
+                    int[] txTimeMs = new int[TX_POWER_LEVELS];
+                    in.readIntArray(txTimeMs);
+                    int rxTimeMs = in.readInt();
+                    return new ActivityStatsTechSpecificInfo(
+                            rat, frequencyRange, txTimeMs, rxTimeMs);
+                }
+
+                public ActivityStatsTechSpecificInfo[] newArray(int size) {
+                    return new ActivityStatsTechSpecificInfo[size];
+                }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mRat);
+        dest.writeInt(mFrequencyRange);
+        dest.writeIntArray(mTxTimeMs);
+        dest.writeInt(mRxTimeMs);
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2c39863..adb51c4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3957,101 +3957,242 @@
     public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG =
             "opportunistic_network_max_backoff_time_long";
 
-    /**
-    * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered good
-    * enough for internet data.
-    *
-    * @hide
-    */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT =
-            "opportunistic_network_entry_threshold_ss_rsrp_int";
+    /** @hide */
+    public static class OpportunisticNetwork {
+        /**
+         * Prefix of all {@code OpportunisticNetwork.KEY_*} constants.
+         *
+         * @hide
+         */
+        public static final String PREFIX = "opportunistic.";
 
-    /**
-    * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered good
-    * enough for internet data.
-    *
-    * @hide
-    */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE =
-            "opportunistic_network_entry_threshold_ss_rsrq_double";
+        /**
+         * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered
+         * good enough for internet data. Note other factors may be considered for the final
+         * decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrp} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT =
+                PREFIX + "entry_threshold_ss_rsrp_int";
 
-    /**
-    * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not
-    * be considered good enough for internet data.
-    *
-    * @hide
-    */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT =
-            "opportunistic_network_exit_threshold_ss_rsrp_int";
+        /**
+         * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the threshold in int.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE =
+                PREFIX + "entry_threshold_ss_rsrp_int_bundle";
 
-    /**
-    * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not
-    * be considered good enough for internet data.
-    *
-    * @hide
-    */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
-            "opportunistic_network_exit_threshold_ss_rsrq_double";
+        /**
+         * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered
+         * good enough for internet data. Note other factors may be considered for the final
+         * decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrq} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE =
+                PREFIX + "entry_threshold_ss_rsrq_double";
 
-    /**
-     * Controls back off time in milliseconds for switching back to
-     * 5G opportunistic subscription. This time will be added to
-     * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
-     * determine hysteresis time if there is ping pong situation
-     * (determined by system app or 1st party app) between primary and 5G opportunistic
-     * subscription. Ping ping situation is defined in
-     * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG.
-     * If ping pong situation continuous #KEY_OPPORTUNISTIC_5G_NETWORK_BACKOFF_TIME_LONG
-     * will be added to previously determined hysteresis time.
-     *
-     * @hide
-     */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG =
-            "opportunistic_network_5g_backoff_time_long";
+        /**
+         * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the threshold in double.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE =
+                PREFIX + "entry_threshold_ss_rsrq_double_bundle";
 
-    /**
-     * Controls the max back off time in milliseconds for switching back to
-     * 5G opportunistic subscription.
-     * This time will be the max hysteresis that can be determined irrespective of there is
-     * continuous ping pong situation or not as described in
-     * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG and
-     * #KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG.
-     *
-     * @hide
-     */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG =
-            "opportunistic_network_5g_max_backoff_time_long";
+        /**
+         * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not
+         * be considered good enough for internet data. Note other factors may be considered
+         * for the final decision.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT =
+                PREFIX + "exit_threshold_ss_rsrp_int";
 
-    /**
-    * Controls the ping pong determination of 5G opportunistic network.
-    * If opportunistic network is determined as out of service or below
-    * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT or
-    * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_INT within
-    * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG of switching to opportunistic network,
-    * it will be determined as ping pong situation by system app or 1st party app.
-     *
-    * @hide
-    */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG =
-            "opportunistic_network_5g_ping_pong_time_long";
+        /**
+         * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT}.
+         *
+         * <p>The syntax of its value is similar to
+         * {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE =
+                PREFIX + "exit_threshold_ss_rsrp_int_bundle";
 
-    /**
-     * Controls hysteresis time in milliseconds for which will be waited before switching
-     * data to a 5G opportunistic network.
-     *
-     * @hide
-     */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG =
-            "opportunistic_network_5g_data_switch_hysteresis_time_long";
+        /**
+         * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not
+         * be considered good enough for internet data. Note other factors may be considered
+         * for the final decision.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
+                PREFIX + "exit_threshold_ss_rsrq_double";
 
-    /**
-     * Controls hysteresis time in milliseconds for which will be waited before switching from
-     * 5G opportunistic network to primary network.
-     *
-     * @hide
-     */
-    public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
-            "opportunistic_network_5g_data_switch_exit_hysteresis_time_long";
+        /**
+         * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE}.
+         *
+         * <p>The syntax of its value is similar to
+         * {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE =
+                PREFIX + "exit_threshold_ss_rsrq_double_bundle";
+
+        /**
+         * Controls hysteresis time in milliseconds for which will be waited before switching
+         * data to a 5G opportunistic network.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG =
+                PREFIX + "5g_data_switch_hysteresis_time_long";
+
+        /**
+         * Similar to {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} but supports
+         * different values for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the time in long.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE =
+                PREFIX + "5g_data_switch_hysteresis_time_long_bundle";
+
+        /**
+         * Controls hysteresis time in milliseconds for which will be waited before switching from
+         * 5G opportunistic network to primary network.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
+                PREFIX + "5g_data_switch_exit_hysteresis_time_long";
+
+        /**
+         * Similar to {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG} but supports
+         * different values for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG}.
+         *
+         * <p>The syntax is similar to
+         * {@link KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE =
+                PREFIX + "5g_data_switch_exit_hysteresis_time_long_bundle";
+
+        /**
+         * Controls back off time in milliseconds for switching back to
+         * 5G opportunistic subscription. This time will be added to
+         * {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+         * determine hysteresis time if there is ping pong situation
+         * (determined by system app or 1st party app) between primary and 5G opportunistic
+         * subscription. Ping ping situation is defined in
+         * {@link #KEY_5G_PING_PONG_TIME_LONG}.
+         * If ping pong situation continuous {@link #KEY_5G_NETWORK_BACKOFF_TIME_LONG}
+         * will be added to previously determined hysteresis time.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_BACKOFF_TIME_LONG =
+                PREFIX + "5g_backoff_time_long";
+
+        /**
+         * Controls the max back off time in milliseconds for switching back to
+         * 5G opportunistic subscription.
+         * This time will be the max hysteresis that can be determined irrespective of there is
+         * continuous ping pong situation or not as described in
+         * {@link #KEY_5G_PING_PONG_TIME_LONG} and
+         * {@link #KEY_5G_BACKOFF_TIME_LONG}.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_MAX_BACKOFF_TIME_LONG =
+                PREFIX + "5g_max_backoff_time_long";
+
+        /**
+         * Controls the ping pong determination of 5G opportunistic network.
+         * If opportunistic network is determined as out of service or below
+         * {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} or
+         * {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} within
+         * {@link #KEY_5G_PING_PONG_TIME_LONG} of switching to opportunistic network,
+         * it will be determined as ping pong situation by system app or 1st party app.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_PING_PONG_TIME_LONG =
+                PREFIX + "5g_ping_pong_time_long";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            // Default value is -111 dBm for all bands.
+            sDefaults.putInt(KEY_ENTRY_THRESHOLD_SS_RSRP_INT, -111);
+            sDefaults.putPersistableBundle(KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE,
+                                           PersistableBundle.EMPTY);
+            // Default value is -18.5 dB for all bands.
+            sDefaults.putDouble(KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+            sDefaults.putPersistableBundle(
+                    KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is -120 dBm for all bands.
+            sDefaults.putInt(KEY_EXIT_THRESHOLD_SS_RSRP_INT, -120);
+            sDefaults.putPersistableBundle(KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE,
+                                           PersistableBundle.EMPTY);
+            // Default value is -18.5 dB for all bands.
+            sDefaults.putDouble(KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+            sDefaults.putPersistableBundle(
+                    KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 2 seconds for all bands.
+            defaults.putLong(KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000);
+            defaults.putPersistableBundle(
+                    KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 2 seconds for all bands.
+            defaults.putLong(KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
+            defaults.putPersistableBundle(
+                    KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 10 seconds.
+            sDefaults.putLong(KEY_5G_BACKOFF_TIME_LONG, 10000);
+            // Default value is 60 seconds.
+            sDefaults.putLong(KEY_5G_MAX_BACKOFF_TIME_LONG, 60000);
+            // Default value is 60 seconds.
+            sDefaults.putLong(KEY_5G_PING_PONG_TIME_LONG, 60000);
+            return defaults;
+        }
+    }
+
     /**
      * Controls whether 4G opportunistic networks should be scanned for possible data switch.
      *
@@ -5395,7 +5536,7 @@
             defaults.putPersistableBundle(
                     KEY_RCS_REQUIRES_PROVISIONING_BUNDLE, new PersistableBundle());
 
-            defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, true);
+            defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, false);
             defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true);
             defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
             defaults.putBoolean(KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL, true);
@@ -5410,7 +5551,7 @@
             defaults.putInt(KEY_SIP_TIMER_H_MILLIS_INT, 128000);
             defaults.putInt(KEY_SIP_TIMER_J_MILLIS_INT, 128000);
             defaults.putInt(KEY_SIP_SERVER_PORT_NUMBER_INT, 5060);
-            defaults.putInt(KEY_REQUEST_URI_TYPE_INT, REQUEST_URI_FORMAT_SIP);
+            defaults.putInt(KEY_REQUEST_URI_TYPE_INT, REQUEST_URI_FORMAT_TEL);
             defaults.putInt(KEY_SIP_PREFERRED_TRANSPORT_INT, PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP);
             defaults.putInt(KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT, 1500);
             defaults.putInt(KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT, 1500);
@@ -6431,11 +6572,11 @@
             defaults.putBoolean(KEY_MULTIENDPOINT_SUPPORTED_BOOL, false);
             defaults.putBoolean(KEY_SESSION_TIMER_SUPPORTED_BOOL, true);
             defaults.putBoolean(KEY_OIP_SOURCE_FROM_HEADER_BOOL, false);
-            defaults.putBoolean(KEY_PRACK_SUPPORTED_FOR_18X_BOOL, true);
+            defaults.putBoolean(KEY_PRACK_SUPPORTED_FOR_18X_BOOL, false);
             defaults.putBoolean(KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL, true);
             defaults.putBoolean(KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
 
-            defaults.putInt(KEY_SESSION_REFRESHER_TYPE_INT, SESSION_REFRESHER_TYPE_UNKNOWN);
+            defaults.putInt(KEY_SESSION_REFRESHER_TYPE_INT, SESSION_REFRESHER_TYPE_UAC);
             defaults.putInt(KEY_SESSION_PRIVACY_TYPE_INT, SESSION_PRIVACY_TYPE_HEADER);
             defaults.putInt(KEY_SESSION_REFRESH_METHOD_INT,
                             SESSION_REFRESH_METHOD_UPDATE_PREFERRED);
@@ -6457,7 +6598,9 @@
                     KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY,
                     new int[] {
                         Ims.RTCP_INACTIVITY_ON_CONNECTED,
-                        Ims.RTP_INACTIVITY_ON_CONNECTED
+                        Ims.RTP_INACTIVITY_ON_CONNECTED,
+                        Ims.E911_RTCP_INACTIVITY_ON_CONNECTED,
+                        Ims.RTCP_INACTIVITY_ON_HOLD
                     });
 
             defaults.putIntArray(
@@ -6821,7 +6964,7 @@
                         AccessNetworkType.IWLAN
                     });
 
-            defaults.putInt(KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT, 20000);
+            defaults.putInt(KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT, 10000);
             defaults.putInt(KEY_REFRESH_GEOLOCATION_TIMEOUT_MILLIS_INT, 5000);
 
             return defaults;
@@ -7494,7 +7637,7 @@
 
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
-            defaults.putBoolean(KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL, true);
+            defaults.putBoolean(KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL, false);
             defaults.putBoolean(KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL, true);
             defaults.putBoolean(KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL, true);
             defaults.putBoolean(KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL, true);
@@ -8124,6 +8267,13 @@
             "telephony_data_handover_retry_rules_string_array";
 
     /**
+     * Indicates whether delay tearing down IMS data network until voice call ends.
+     * @hide
+     */
+    public static final String KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL =
+            "delay_ims_tear_down_until_call_end_bool";
+
+    /**
      * The patterns of missed incoming call sms. This is the regular expression used for
      * matching the missed incoming call's date, time, and caller id. The pattern should match
      * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
@@ -8217,7 +8367,9 @@
             "store_sim_pin_for_unattended_reboot_bool";
 
     /**
-     * Determine whether "Enable 2G" toggle can be shown.
+     * Allow whether the user can use the "Allow 2G" toggle in Settings.
+     *
+     * If {@code true} then the toggle is disabled (i.e. grayed out).
      *
      * Used to trade privacy/security against potentially reduced carrier coverage for some
      * carriers.
@@ -8854,6 +9006,7 @@
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000);
         /* Default value is 3 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000);
+        sDefaults.putAll(OpportunisticNetwork.getDefaults());
         sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true);
         sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true);
         /* Default value is 60 seconds. */
@@ -8862,24 +9015,6 @@
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
         /* Default value is 60 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
-        /* Default value is -111 dBm. */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT, -111);
-        /* Default value is -18.5 dB. */
-        sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
-        /* Default value is -120 dBm. */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT, -120);
-        /* Default value is -18.5 dB. */
-        sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
-        /* Default value is 10 seconds. */
-        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG, 10000);
-        /* Default value is 60 seconds. */
-        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG, 60000);
-        /* Default value is 60 seconds. */
-        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG, 60000);
-        /* Default value is 2 seconds. */
-        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000);
-        /* Default value is 2 seconds. */
-        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
         sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
         sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
         sDefaults.putLong(
@@ -8968,6 +9103,7 @@
                 KEY_TELEPHONY_DATA_HANDOVER_RETRY_RULES_STRING_ARRAY, new String[] {
                         "retry_interval=1000|2000|4000|8000|16000, maximum_retries=5"
                 });
+        sDefaults.putBoolean(KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL, false);
         sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
         sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 837124f..ca6dc2d 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Objects;
 
 
@@ -76,7 +78,8 @@
     /**
      * @hide
      */
-    DataSpecificRegistrationInfo(
+    @VisibleForTesting
+    public DataSpecificRegistrationInfo(
             int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
             boolean isEnDcAvailable, @Nullable VopsSupportInfo vops) {
         this.maxDataCalls = maxDataCalls;
@@ -186,7 +189,7 @@
     /**
      * @return The VOPS (Voice over Packet Switched) support information.
      *
-     * The instance of {@link LTEVopsSupportInfo}, or {@link NrVopsSupportInfo},
+     * The instance of {@link LteVopsSupportInfo}, or {@link NrVopsSupportInfo},
      * null if there is there is no VOPS support information available.
      */
     @Nullable
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index ec6c25d..730a9d1 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -25,6 +25,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.telephony.ServiceState.FrequencyRange;
 import android.util.Range;
 
 import java.lang.annotation.Retention;
@@ -94,8 +95,10 @@
     private long mTimestamp;
     private int mSleepTimeMs;
     private int mIdleTimeMs;
-    private int[] mTxTimeMs;
-    private int mRxTimeMs;
+    private int[] mTotalTxTimeMs;
+    private int mTotalRxTimeMs;
+    private int mSizeOfSpecificInfo;
+    private ActivityStatsTechSpecificInfo[] mActivityStatsTechSpecificInfo;
 
     /**
      * @hide
@@ -110,8 +113,17 @@
         mTimestamp = timestamp;
         mSleepTimeMs = sleepTimeMs;
         mIdleTimeMs = idleTimeMs;
-        mTxTimeMs = txTimeMs;
-        mRxTimeMs = rxTimeMs;
+        mTotalTxTimeMs = txTimeMs;
+        mTotalRxTimeMs = rxTimeMs;
+
+        mActivityStatsTechSpecificInfo = new ActivityStatsTechSpecificInfo[1];
+        mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
+        mActivityStatsTechSpecificInfo[0] =
+                new ActivityStatsTechSpecificInfo(
+                        AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+                        ServiceState.FREQUENCY_RANGE_UNKNOWN,
+                        txTimeMs,
+                        rxTimeMs);
     }
 
     /**
@@ -124,14 +136,49 @@
         this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
     }
 
+    /** @hide */
+    public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+                        @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
+        mTimestamp = timestamp;
+        mSleepTimeMs = sleepTimeMs;
+        mIdleTimeMs = idleTimeMs;
+        mActivityStatsTechSpecificInfo = activityStatsTechSpecificInfo;
+        mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
+        mTotalTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        for (int i = 0; i < getNumTxPowerLevels(); i++) {
+            for (int j = 0; j < getSpecificInfoLength(); j++) {
+                mTotalTxTimeMs[i] = mTotalTxTimeMs[i]
+                            + (int) mActivityStatsTechSpecificInfo[j].getTransmitTimeMillis(i);
+            }
+        }
+        mTotalRxTimeMs = 0;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            mTotalRxTimeMs =
+                    mTotalRxTimeMs + (int) mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+        }
+    }
+
+    /**
+     * Provided for convenience in manipulation since the API exposes long values but internal
+     * representations are ints.
+     * @hide
+     */
+    public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+                        @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
+        this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, activityStatsTechSpecificInfo);
+    }
+
     @Override
     public String toString() {
         return "ModemActivityInfo{"
-            + " mTimestamp=" + mTimestamp
-            + " mSleepTimeMs=" + mSleepTimeMs
-            + " mIdleTimeMs=" + mIdleTimeMs
-            + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
-            + " mRxTimeMs=" + mRxTimeMs
+            + " mTimestamp="
+            + mTimestamp
+            + " mSleepTimeMs="
+            + mSleepTimeMs
+            + " mIdleTimeMs="
+            + mIdleTimeMs
+            + " mActivityStatsTechSpecificInfo="
+            + Arrays.toString(mActivityStatsTechSpecificInfo)
             + "}";
     }
 
@@ -145,11 +192,17 @@
             long timestamp = in.readLong();
             int sleepTimeMs = in.readInt();
             int idleTimeMs = in.readInt();
-            int[] txTimeMs = new int[TX_POWER_LEVELS];
-            in.readIntArray(txTimeMs);
-            int rxTimeMs = in.readInt();
-            return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
-                                txTimeMs, rxTimeMs);
+            Parcelable[] tempSpecifiers =
+                    in.createTypedArray(ActivityStatsTechSpecificInfo.CREATOR);
+            ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo;
+            activityStatsTechSpecificInfo =
+                    new ActivityStatsTechSpecificInfo[tempSpecifiers.length];
+            for (int i = 0; i < tempSpecifiers.length; i++) {
+                activityStatsTechSpecificInfo[i] =
+                                (ActivityStatsTechSpecificInfo) tempSpecifiers[i];
+                    }
+            return new ModemActivityInfo(
+                    timestamp, sleepTimeMs, idleTimeMs, activityStatsTechSpecificInfo);
         }
 
         public ModemActivityInfo[] newArray(int size) {
@@ -165,15 +218,14 @@
         dest.writeLong(mTimestamp);
         dest.writeInt(mSleepTimeMs);
         dest.writeInt(mIdleTimeMs);
-        dest.writeIntArray(mTxTimeMs);
-        dest.writeInt(mRxTimeMs);
+        dest.writeTypedArray(mActivityStatsTechSpecificInfo, flags);
     }
 
     /**
      * Gets the timestamp at which this modem activity info was recorded.
      *
-     * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this
-     * {@link ModemActivityInfo} was recorded.
+     * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this {@link
+     *     ModemActivityInfo} was recorded.
      */
     public @ElapsedRealtimeLong long getTimestampMillis() {
         return mTimestamp;
@@ -188,14 +240,41 @@
      * Gets the amount of time the modem spent transmitting at a certain power level.
      *
      * @param powerLevel The power level to query.
-     * @return The amount of time, in milliseconds, that the modem spent transmitting at the
-     * given power level.
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the given
+     *     power level.
      */
     public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
             @TxPowerLevel int powerLevel) {
-        return mTxTimeMs[powerLevel];
+        long txTimeMsAtPowerLevel = 0;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            txTimeMsAtPowerLevel +=
+                    mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+        }
+        return txTimeMsAtPowerLevel;
     }
 
+    /** @hide */
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel, int rat) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+            }
+        }
+        return 0;
+    }
+
+    /** @hide */
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel, int rat, @FrequencyRange int freq) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+            }
+        }
+        return 0;
+    }
     /**
      * Gets the range of transmit powers corresponding to a certain power level.
      *
@@ -208,17 +287,64 @@
     }
 
     /** @hide */
-    public void setTransmitTimeMillis(int[] txTimeMs) {
-        mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
+    public int getSpecificInfoRat(int index) {
+        return mActivityStatsTechSpecificInfo[index].getRat();
     }
 
+    /** @hide */
+    public int getSpecificInfoFrequencyRange(int index) {
+        return mActivityStatsTechSpecificInfo[index].getFrequencyRange();
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int[] txTimeMs) {
+        mTotalTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int rat, int[] txTimeMs) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
+            }
+        }
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int rat, int freq, int[] txTimeMs) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
+            }
+        }
+    }
     /**
      * @return The raw array of transmit power durations
      * @hide
      */
     @NonNull
     public int[] getTransmitTimeMillis() {
-        return mTxTimeMs;
+        return mTotalTxTimeMs;
+    }
+
+    /** @hide */
+    public int[] getTransmitTimeMillis(@AccessNetworkConstants.RadioAccessNetworkType int rat) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
+            }
+        }
+        return new int[5];
+    }
+
+    /** @hide */
+    public int[] getTransmitTimeMillis(
+            @AccessNetworkConstants.RadioAccessNetworkType int rat, @FrequencyRange int freq) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
+            }
+        }
+        return new int[5];
     }
 
     /**
@@ -238,6 +364,7 @@
     /**
      * Provided for convenience, since the API surface needs to return longs but internal
      * representations are ints.
+     *
      * @hide
      */
     public void setSleepTimeMillis(long sleepTimeMillis) {
@@ -257,14 +384,63 @@
      */
     public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
         int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
-            txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i];
+
+        ActivityStatsTechSpecificInfo[] mDeltaSpecificInfo;
+        mDeltaSpecificInfo = new ActivityStatsTechSpecificInfo[other.getSpecificInfoLength()];
+
+        boolean matched;
+        for (int i = 0; i < other.getSpecificInfoLength(); i++) {
+            matched = false;
+            for (int j = 0; j < getSpecificInfoLength(); j++) {
+                int rat = mActivityStatsTechSpecificInfo[j].getRat();
+                if (rat == other.mActivityStatsTechSpecificInfo[i].getRat() && !matched) {
+                    if (mActivityStatsTechSpecificInfo[j].getRat()
+                            == AccessNetworkConstants.AccessNetworkType.NGRAN) {
+                        if (other.mActivityStatsTechSpecificInfo[i].getFrequencyRange()
+                                == mActivityStatsTechSpecificInfo[j].getFrequencyRange()) {
+                            int freq = mActivityStatsTechSpecificInfo[j].getFrequencyRange();
+                            for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                                txTimeMs[lvl] =
+                                        (int) (other.getTransmitDurationMillisAtPowerLevel(
+                                                            lvl, rat, freq)
+                                                        - getTransmitDurationMillisAtPowerLevel(
+                                                            lvl, rat, freq));
+                            }
+                            matched = true;
+                            mDeltaSpecificInfo[i] =
+                                    new ActivityStatsTechSpecificInfo(
+                                            rat,
+                                            freq,
+                                            txTimeMs,
+                                            (int) (other.getReceiveTimeMillis(rat, freq)
+                                                        - getReceiveTimeMillis(rat, freq)));
+                        }
+                    } else {
+                        for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                            txTimeMs[lvl] =
+                                    (int) (other.getTransmitDurationMillisAtPowerLevel(lvl, rat)
+                                                - getTransmitDurationMillisAtPowerLevel(lvl, rat));
+                        }
+                        matched = true;
+                        mDeltaSpecificInfo[i] =
+                                new ActivityStatsTechSpecificInfo(
+                                        rat,
+                                        ServiceState.FREQUENCY_RANGE_UNKNOWN,
+                                        txTimeMs,
+                                        (int) (other.getReceiveTimeMillis(rat)
+                                                     - getReceiveTimeMillis(rat)));
+                    }
+                }
+            }
+            if (!matched) {
+                mDeltaSpecificInfo[i] = other.mActivityStatsTechSpecificInfo[i];
+            }
         }
-        return new ModemActivityInfo(other.getTimestampMillis(),
+        return new ModemActivityInfo(
+                other.getTimestampMillis(),
                 other.getSleepTimeMillis() - getSleepTimeMillis(),
                 other.getIdleTimeMillis() - getIdleTimeMillis(),
-                txTimeMs,
-                other.getReceiveTimeMillis() - getReceiveTimeMillis());
+                mDeltaSpecificInfo);
     }
 
     /**
@@ -285,6 +461,7 @@
     /**
      * Provided for convenience, since the API surface needs to return longs but internal
      * representations are ints.
+     *
      * @hide
      */
     public void setIdleTimeMillis(long idleTimeMillis) {
@@ -297,21 +474,66 @@
      * @return Time in milliseconds.
      */
     public @DurationMillisLong long getReceiveTimeMillis() {
-        return mRxTimeMs;
+        return mTotalRxTimeMs;
+    }
+
+    /** @hide */
+    public @DurationMillisLong long getReceiveTimeMillis(int rat) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+            }
+        }
+        return 0;
+    }
+    /** @hide */
+    public @DurationMillisLong long getReceiveTimeMillis(int rat, int freq) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+            }
+        }
+        return 0;
     }
 
     /** @hide */
     public void setReceiveTimeMillis(int rxTimeMillis) {
-        mRxTimeMs = rxTimeMillis;
+        mTotalRxTimeMs = rxTimeMillis;
     }
 
     /**
      * Provided for convenience, since the API surface needs to return longs but internal
      * representations are ints.
+     *
      * @hide
      */
     public void setReceiveTimeMillis(long receiveTimeMillis) {
-        mRxTimeMs = (int) receiveTimeMillis;
+        mTotalRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int rat, long receiveTimeMillis) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
+            }
+        }
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int rat, int freq, long receiveTimeMillis) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
+            }
+        }
+    }
+
+    /** @hide */
+    public int getSpecificInfoLength() {
+        return mSizeOfSpecificInfo;
     }
 
     /**
@@ -323,23 +545,42 @@
      */
     @TestApi
     public boolean isValid() {
-        boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
-
-        return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
-                && (getReceiveTimeMillis() >= 0) && !isEmpty());
+        if (mActivityStatsTechSpecificInfo == null) {
+            return false;
+        } else {
+            boolean isTxPowerValid = true;
+            boolean isRxPowerValid = true;
+            for (int i = 0; i < getSpecificInfoLength(); i++) {
+                if (!mActivityStatsTechSpecificInfo[i].isTxPowerValid()) {
+                    isTxPowerValid = false;
+                }
+                if (!mActivityStatsTechSpecificInfo[i].isRxPowerValid()) {
+                    isRxPowerValid = false;
+                }
+            }
+            return isTxPowerValid
+                    && isRxPowerValid
+                    && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && !isEmpty());
+        }
     }
 
     /** @hide */
     @TestApi
     public boolean isEmpty() {
-        boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
-                || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
-
-        return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
-                && (getReceiveTimeMillis() == 0));
+        boolean isTxPowerEmpty = false;
+        boolean isRxPowerEmpty = false;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].isTxPowerEmpty()) {
+                isTxPowerEmpty = true;
+            }
+            if (mActivityStatsTechSpecificInfo[i].isRxPowerEmpty()) {
+                isRxPowerEmpty = true;
+            }
+        }
+        return isTxPowerEmpty
+                && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && isRxPowerEmpty);
     }
 
-
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -348,14 +589,15 @@
         return mTimestamp == that.mTimestamp
                 && mSleepTimeMs == that.mSleepTimeMs
                 && mIdleTimeMs == that.mIdleTimeMs
-                && mRxTimeMs == that.mRxTimeMs
-                && Arrays.equals(mTxTimeMs, that.mTxTimeMs);
+                && mSizeOfSpecificInfo == that.mSizeOfSpecificInfo
+                && Arrays.equals(
+                        mActivityStatsTechSpecificInfo, that.mActivityStatsTechSpecificInfo);
     }
 
     @Override
     public int hashCode() {
-        int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs);
-        result = 31 * result + Arrays.hashCode(mTxTimeMs);
+        int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mTotalRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTotalTxTimeMs);
         return result;
     }
 }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index c18443e..c701e44 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -242,13 +242,16 @@
      * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
      * information is not available.
      * @param rplmn the registered plmn or the last plmn for attempted registration if reg failed.
+     * @param voiceSpecificInfo Voice specific registration information.
+     * @param dataSpecificInfo Data specific registration information.
      */
     private NetworkRegistrationInfo(@Domain int domain, @TransportType int transportType,
-                                   @RegistrationState int registrationState,
-                                   @NetworkType int accessNetworkTechnology, int rejectCause,
-                                   boolean emergencyOnly,
-                                   @Nullable @ServiceType List<Integer> availableServices,
-                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn) {
+            @RegistrationState int registrationState,
+            @NetworkType int accessNetworkTechnology, int rejectCause,
+            boolean emergencyOnly, @Nullable @ServiceType List<Integer> availableServices,
+            @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+            @Nullable VoiceSpecificRegistrationInfo voiceSpecificInfo,
+            @Nullable DataSpecificRegistrationInfo dataSpecificInfo) {
         mDomain = domain;
         mTransportType = transportType;
         mRegistrationState = registrationState;
@@ -262,6 +265,10 @@
         mEmergencyOnly = emergencyOnly;
         mNrState = NR_STATE_NONE;
         mRplmn = rplmn;
+        mVoiceSpecificInfo = voiceSpecificInfo;
+        mDataSpecificInfo = dataSpecificInfo;
+
+        updateNrState();
     }
 
     /**
@@ -276,10 +283,9 @@
                                    boolean cssSupported, int roamingIndicator, int systemIsInPrl,
                                    int defaultRoamingIndicator) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
-                emergencyOnly, availableServices, cellIdentity, rplmn);
-
-        mVoiceSpecificInfo = new VoiceSpecificRegistrationInfo(cssSupported, roamingIndicator,
-                systemIsInPrl, defaultRoamingIndicator);
+                emergencyOnly, availableServices, cellIdentity, rplmn,
+                new VoiceSpecificRegistrationInfo(cssSupported, roamingIndicator,
+                        systemIsInPrl, defaultRoamingIndicator), null);
     }
 
     /**
@@ -295,11 +301,9 @@
                                    boolean isNrAvailable, boolean isEndcAvailable,
                                    @Nullable VopsSupportInfo vopsSupportInfo) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
-                emergencyOnly, availableServices, cellIdentity, rplmn);
-        mDataSpecificInfo = new DataSpecificRegistrationInfo(
-                maxDataCalls, isDcNrRestricted, isNrAvailable,
-                isEndcAvailable, vopsSupportInfo);
-        updateNrState();
+                emergencyOnly, availableServices, cellIdentity, rplmn, null,
+                new DataSpecificRegistrationInfo(maxDataCalls, isDcNrRestricted, isNrAvailable,
+                        isEndcAvailable, vopsSupportInfo));
     }
 
     private NetworkRegistrationInfo(Parcel source) {
@@ -804,6 +808,12 @@
         @NonNull
         private String mRplmn = "";
 
+        @Nullable
+        private DataSpecificRegistrationInfo mDataSpecificRegistrationInfo;
+
+        @Nullable
+        private VoiceSpecificRegistrationInfo mVoiceSpecificRegistrationInfo;
+
         /**
          * Default constructor for Builder.
          */
@@ -930,6 +940,30 @@
         }
 
         /**
+         * Set voice specific registration information.
+         *
+         * @param info The voice specific registration information.
+         * @return The builder.
+         * @hide
+         */
+        public @NonNull Builder setVoiceSpecificInfo(@NonNull VoiceSpecificRegistrationInfo info) {
+            mVoiceSpecificRegistrationInfo = info;
+            return this;
+        }
+
+        /**
+         * Set data specific registration information.
+         *
+         * @param info The data specific registration information.
+         * @return The builder.
+         * @hide
+         */
+        public @NonNull Builder setDataSpecificInfo(@NonNull DataSpecificRegistrationInfo info) {
+            mDataSpecificRegistrationInfo = info;
+            return this;
+        }
+
+        /**
          * Build the NetworkRegistrationInfo.
          * @return the NetworkRegistrationInfo object.
          * @hide
@@ -938,7 +972,8 @@
         public @NonNull NetworkRegistrationInfo build() {
             return new NetworkRegistrationInfo(mDomain, mTransportType, mRegistrationState,
                     mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
-                    mCellIdentity, mRplmn);
+                    mCellIdentity, mRplmn, mVoiceSpecificRegistrationInfo,
+                    mDataSpecificRegistrationInfo);
         }
     }
 }
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 95448c7..d91134e 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -23,12 +23,15 @@
 import android.os.Parcelable;
 import android.telephony.Annotation.NetworkType;
 
+import com.android.telephony.Rlog;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Objects;
 
 public final class PhysicalChannelConfig implements Parcelable {
+    static final String TAG = "PhysicalChannelConfig";
 
     // TODO(b/72993578) consolidate these enums in a central location.
     /** @hide */
@@ -568,19 +571,21 @@
 
         public @NonNull Builder setNetworkType(@NetworkType int networkType) {
             if (!TelephonyManager.isNetworkTypeValid(networkType)) {
-                throw new IllegalArgumentException("Network type: " + networkType + " is invalid.");
+                Rlog.e(TAG, "Builder.setNetworkType: Network type " + networkType + " is invalid.");
+            } else {
+                mNetworkType = networkType;
             }
-            mNetworkType = networkType;
             return this;
         }
 
         public @NonNull Builder setFrequencyRange(int frequencyRange) {
             if (!ServiceState.isFrequencyRangeValid(frequencyRange)
                     && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
-                throw new IllegalArgumentException("Frequency range: " + frequencyRange +
-                        " is invalid.");
+                Rlog.e(TAG, "Builder.setFrequencyRange: Frequency range " + frequencyRange
+                        + " is invalid.");
+            } else {
+                mFrequencyRange = frequencyRange;
             }
-            mFrequencyRange = frequencyRange;
             return this;
         }
 
@@ -596,19 +601,21 @@
 
         public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
             if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) {
-                throw new IllegalArgumentException("Cell downlink bandwidth(kHz): " +
-                        cellBandwidthDownlinkKhz + " is invalid.");
+                Rlog.e(TAG, "Builder.setCellBandwidthDownlinkKhz: Cell downlink bandwidth(kHz) "
+                        + cellBandwidthDownlinkKhz + " is invalid.");
+            } else {
+                mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
             }
-            mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
             return this;
         }
 
         public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) {
             if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) {
-                throw new IllegalArgumentException("Cell uplink bandwidth(kHz): "+
-                        cellBandwidthUplinkKhz +" is invalid.");
+                Rlog.e(TAG, "Builder.setCellBandwidthUplinkKhz: Cell uplink bandwidth(kHz) "
+                        + cellBandwidthUplinkKhz + " is invalid.");
+            } else {
+                mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz;
             }
-            mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz;
             return this;
         }
 
@@ -625,19 +632,20 @@
 
         public @NonNull Builder setPhysicalCellId(int physicalCellId) {
             if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) {
-                throw new IllegalArgumentException("Physical cell Id: " + physicalCellId +
-                        " is over limit.");
+                Rlog.e(TAG, "Builder.setPhysicalCellId: Physical cell ID " + physicalCellId
+                        + " is over limit.");
+            } else {
+                mPhysicalCellId = physicalCellId;
             }
-            mPhysicalCellId = physicalCellId;
             return this;
         }
 
         public @NonNull Builder setBand(int band) {
             if (band <= BAND_UNKNOWN) {
-                throw new IllegalArgumentException("Band: " + band +
-                        " is invalid.");
+                Rlog.e(TAG, "Builder.setBand: Band " + band + " is invalid.");
+            } else {
+                mBand = band;
             }
-            mBand = band;
             return this;
         }
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 65f5632..6fe9bf9 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1205,7 +1205,15 @@
         }
     }
 
-    private void init() {
+    /**
+     * Initialize the service state. Set everything to the default value.
+     *
+     * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
+     * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
+     * is on AP-assisted mode, where IWLAN should be reported through WLAN.
+     * {@link NetworkRegistrationInfo}.
+     */
+    private void init(boolean legacyMode) {
         if (DBG) Rlog.d(LOG_TAG, "init");
         mVoiceRegState = STATE_OUT_OF_SERVICE;
         mDataRegState = STATE_OUT_OF_SERVICE;
@@ -1237,6 +1245,13 @@
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
                     .build());
+            if (!legacyMode) {
+                addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                        .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                        .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                        .build());
+            }
         }
         mOperatorAlphaLongRaw = null;
         mOperatorAlphaShortRaw = null;
@@ -1245,15 +1260,32 @@
     }
 
     public void setStateOutOfService() {
-        init();
+        init(true);
     }
 
     public void setStateOff() {
-        init();
+        init(true);
         mVoiceRegState = STATE_POWER_OFF;
         mDataRegState = STATE_POWER_OFF;
     }
 
+    /**
+     * Set the service state to out-of-service
+     *
+     * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
+     * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
+     * is on AP-assisted mode, where IWLAN should be reported through WLAN.
+     * @param powerOff {@code true} if this is a power off case (i.e. Airplane mode on).
+     * @hide
+     */
+    public void setOutOfService(boolean legacyMode, boolean powerOff) {
+        init(legacyMode);
+        if (powerOff) {
+            mVoiceRegState = STATE_POWER_OFF;
+            mDataRegState = STATE_POWER_OFF;
+        }
+    }
+
     public void setState(int state) {
         setVoiceRegState(state);
         if (DBG) Rlog.e(LOG_TAG, "[ServiceState] setState deprecated use setVoiceRegState()");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 613b0a6..ad04952 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6967,6 +6967,11 @@
      *
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
+     * It is strongly recommended that callers of this should firstly create a new TelephonyManager
+     * instance by calling {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so
+     * can result in unpredictable and detrimental behavior like callers can end up talking to the
+     * wrong SIM card.
+     *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -7060,6 +7065,8 @@
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
+        } catch (IllegalStateException ex) {
+            Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
         }
         return false;
     }
@@ -7107,6 +7114,10 @@
      * Closes a previously opened logical channel to the ICC card.
      *
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
+     * It is strongly recommended that callers of this API should firstly create
+     * new TelephonyManager instance by calling
+     * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in
+     * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -7115,6 +7126,7 @@
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
+     * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean iccCloseLogicalChannel(int channel) {
@@ -7122,8 +7134,6 @@
             return iccCloseLogicalChannel(getSubId(), channel);
         } catch (IllegalStateException ex) {
             Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
-        } catch (IllegalArgumentException ex) {
-            Rlog.e(TAG, "iccCloseLogicalChannel IllegalArgumentException", ex);
         }
         return false;
     }
@@ -7154,6 +7164,8 @@
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
+        } catch (IllegalStateException ex) {
+            Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
         }
         return false;
     }
@@ -7255,6 +7267,11 @@
      *
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
+     * It is strongly recommended that callers of this API should firstly create a new
+     * TelephonyManager instance by calling
+     * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in
+     * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card.
+     *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -7647,7 +7664,7 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
-     * {@link #resetRadioConfig()} for reset type 3
+     * {@link #resetRadioConfig()} for reset type 3 (b/116476729)
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a49a61b5..b6ae530 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -25,15 +25,20 @@
 import android.annotation.SystemApi;
 import android.app.Activity;
 import android.app.PendingIntent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccCardManager.ResetOption;
+import android.util.Log;
 
 import com.android.internal.telephony.euicc.IEuiccController;
 
@@ -57,6 +62,7 @@
  */
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
 public class EuiccManager {
+    private static final String TAG = "EuiccManager";
 
     /**
      * Intent action to launch the embedded SIM (eUICC) management settings screen.
@@ -811,6 +817,14 @@
      */
     public static final int ERROR_INVALID_PORT = 10017;
 
+    /**
+     * Apps targeting on Android T and beyond will get exception whenever switchToSubscription
+     * without portIndex is called for disable subscription.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE = 218393363L;
 
     private final Context mContext;
     private int mCardId;
@@ -1127,7 +1141,7 @@
      * intent to prompt the user to accept the download. The caller should also be authorized to
      * manage the subscription to be enabled.
      *
-     * <p> From Android T, devices might support MEP(Multiple Enabled Profile), the subscription
+     * <p> From Android T, devices might support MEP(Multiple Enabled Profiles), the subscription
      * can be installed on different port from the eUICC. Calling apps with carrier privilege
      * (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently active subscriptions
      * can use {@link #switchToSubscription(int, int, PendingIntent)} to specify which port to
@@ -1138,10 +1152,12 @@
      *
      * @param subscriptionId the ID of the subscription to enable. May be
      *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
-     *     current profile without activating another profile to replace it. If it's a disable
-     *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
-     *     permission, or the calling app must be authorized to manage the active subscription on
-     *     the target eUICC.
+     *     current profile without activating another profile to replace it. Calling apps targeting
+     *     on android T must use {@link #switchToSubscription(int, int, PendingIntent)} API for
+     *     disable profile, port index can be found from {@link SubscriptionInfo#getPortIndex()}.
+     *     If it's a disable operation, requires the
+     *     {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or the
+     *     calling app must be authorized to manage the active subscription on the target eUICC.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
@@ -1151,6 +1167,18 @@
             return;
         }
         try {
+            // TODO: Uncomment below compat change code once callers are ported to use
+            //  switchToSubscription with portIndex for disable operation.
+            // if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+            //        && getIEuiccController().isCompatChangeEnabled(mContext.getOpPackageName(),
+            //        SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE)) {
+            //    // Apps targeting on Android T and beyond will get exception whenever
+            //    // switchToSubscription without portIndex is called with INVALID_SUBSCRIPTION_ID.
+            //    Log.e(TAG, "switchToSubscription without portIndex is not allowed for"
+            //            + " disable operation");
+            //    throw new IllegalArgumentException("Must use switchToSubscription with portIndex"
+            //            + " API for disable operation");
+            // }
             getIEuiccController().switchToSubscription(mCardId,
                     subscriptionId, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
@@ -1180,7 +1208,10 @@
      *     current profile without activating another profile to replace it. If it's a disable
      *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
      *     permission, or the calling app must be authorized to manage the active subscription on
-     *     the target eUICC.
+     *     the target eUICC. From Android T, multiple enabled profiles is supported. Calling apps
+     *     targeting on android T must use {@link #switchToSubscription(int, int, PendingIntent)}
+     *     API for disable profile, port index can be found from
+     *     {@link SubscriptionInfo#getPortIndex()}.
      * @param portIndex the index of the port to target for the enabled subscription
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
@@ -1192,6 +1223,17 @@
             return;
         }
         try {
+            boolean canWriteEmbeddedSubscriptions = mContext.checkCallingOrSelfPermission(
+                    Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+                    == PackageManager.PERMISSION_GRANTED;
+            // If the caller is not privileged caller and does not have the carrier privilege over
+            // any active subscription, do not continue.
+            if (!canWriteEmbeddedSubscriptions && !getIEuiccController()
+                    .hasCarrierPrivilegesForPackageOnAnyPhone(mContext.getOpPackageName())) {
+                Log.e(TAG, "Not permitted to use switchToSubscription with portIndex");
+                throw new SecurityException(
+                        "Must have carrier privileges to use switchToSubscription with portIndex");
+            }
             getIEuiccController().switchToSubscriptionWithPort(mCardId,
                     subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
         } catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3415868..783c0d1 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -497,6 +497,8 @@
 
         try {
             return imsRcsController.isCapable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -534,6 +536,8 @@
 
         try {
             return imsRcsController.isAvailable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
             throw new ImsException("Remote IMS Service is not available",
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index dda95b1..19f1a5b 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -55,4 +55,6 @@
     List<String> getSupportedCountries(boolean isSupported);
     boolean isSupportedCountry(String countryIso);
     boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage);
+    boolean hasCarrierPrivilegesForPackageOnAnyPhone(String callingPackage);
+    boolean isCompatChangeEnabled(String callingPackage, long changeId);
 }
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 98d13e8..566c725 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -26,16 +26,8 @@
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6600s" />
         <option name="hidden-api-checks" value="false" />
-        <option name="device-listeners"
-                value="com.android.server.wm.flicker.TraceFileReadyListener" />
     </test>
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="(\w)+\.winscope" />
-        <option name="pull-pattern-keys" value="(\w)+\.mp4" />
-        <option name="collect-on-run-ended-only" value="false" />
-        <option name="clean-up" value="true" />
-    </metrics_collector>
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/sdcard/flicker" />
         <option name="collect-on-run-ended-only" value="true" />
         <option name="clean-up" value="true" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8d60466..4cddd85 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -60,7 +60,7 @@
         }
         teardown {
             test {
-                testApp.exit()
+                testApp.exit(wmHelper)
             }
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 7ee6451..5bd365c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -64,7 +64,6 @@
             device.waitForIdle()
         } else {
             wmHelper.waitImeShown()
-            wmHelper.waitForAppTransitionIdle()
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index b66c45c7..a135e0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -53,8 +53,8 @@
         button.click()
 
         device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
-        wmHelper.waitForFullScreenApp(secondActivityComponent)
         wmHelper.waitFor(
+            WindowManagerStateHelper.isAppFullScreen(secondActivityComponent),
             WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
             WindowManagerConditionsFactory.hasLayersAnimating().negate()
         )
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 429541c..71e576a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -44,6 +44,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 219749605)
 class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@@ -119,4 +120,4 @@
                     )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 5f0176e..a9564fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -61,6 +61,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group2
+@FlakyTest(bugId = 219757170)
 class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 19e2c92..7e3ed82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
@@ -35,6 +36,8 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -64,12 +67,22 @@
                 eachRun {
                     this.setRotation(testSpec.startRotation)
                     testApp.launchViaIntent(wmHelper)
-                    wmHelper.waitForFullScreenApp(testApp.component)
-                    wmHelper.waitForAppTransitionIdle()
+                    val testAppVisible = wmHelper.waitFor(
+                        WindowManagerStateHelper.isAppFullScreen(testApp.component),
+                        WindowManagerConditionsFactory.isAppTransitionIdle(
+                            Display.DEFAULT_DISPLAY))
+                    require(testAppVisible) {
+                        "Expected ${testApp.component.toWindowName()} to be visible"
+                    }
 
                     imeTestApp.launchViaIntent(wmHelper)
-                    wmHelper.waitForFullScreenApp(testApp.component)
-                    wmHelper.waitForAppTransitionIdle()
+                    val imeAppVisible = wmHelper.waitFor(
+                        WindowManagerStateHelper.isAppFullScreen(imeTestApp.component),
+                        WindowManagerConditionsFactory.isAppTransitionIdle(
+                            Display.DEFAULT_DISPLAY))
+                    require(imeAppVisible) {
+                        "Expected ${imeTestApp.component.toWindowName()} to be visible"
+                    }
 
                     imeTestApp.openIME(device, wmHelper)
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index b5e13be..cc808a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.entireScreenCovered
@@ -30,7 +31,9 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -77,14 +80,16 @@
             }
             teardown {
                 test {
-                    testApp.exit()
+                    testApp.exit(wmHelper)
                 }
             }
             transitions {
                 testApp.openSecondActivity(device, wmHelper)
                 device.pressBack()
-                wmHelper.waitForAppTransitionIdle()
-                wmHelper.waitForFullScreenApp(testApp.component)
+                val firstActivityVisible = wmHelper.waitFor(
+                    WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+                    WindowManagerStateHelper.isAppFullScreen(testApp.component))
+                require(firstActivityVisible) { "Expected ${testApp.component} to be visible" }
             }
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 065b1c2..f834820 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,16 +16,19 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
-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.flicker.helpers.setRotation
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,7 +57,13 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
-class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppFromLauncherTransition(testSpec) {
+open class OpenAppColdTest(testSpec: FlickerTestParameter)
+    : OpenAppFromLauncherTransition(testSpec) {
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt
new file mode 100644
index 0000000..0d2869c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest_ShellTransit.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launching an app from launcher
+ *
+ * To run this test: `atest FlickerTests:OpenAppColdTest`
+ *
+ * Actions:
+ *     Make sure no apps are running on the device
+ *     Launch an app [testApp] and wait animation to complete
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [OpenAppTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@FlakyTest(bugId = 219688533)
+class OpenAppColdTest_ShellTransit(testSpec: FlickerTestParameter) : OpenAppColdTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
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 797919b..00fee82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,22 +16,22 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.platform.test.annotations.Presubmit
-import android.view.Display
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Display
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group1
-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.flicker.helpers.reopenAppFromOverview
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,8 +62,13 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
-class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
+open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
     : OpenAppFromLauncherTransition(testSpec) {
+    @Before
+    open fun before() {
+        assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
@@ -134,37 +139,6 @@
     @Test
     override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
 
-    /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun appWindowReplacesLauncherAsTopWindow() {
-        assumeFalse(isShellTransitionsEnabled)
-        super.appWindowReplacesLauncherAsTopWindow()
-    }
-
-    @FlakyTest(bugId = 216266712)
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow_shellTransit() {
-        assumeTrue(isShellTransitionsEnabled)
-        super.appWindowReplacesLauncherAsTopWindow()
-    }
-
-    /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        assumeFalse(isShellTransitionsEnabled)
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
-    /** {@inheritDoc} */
-    @FlakyTest(bugId = 218470989)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry_shellTransit() {
-        assumeTrue(isShellTransitionsEnabled)
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt
new file mode 100644
index 0000000..1c06495
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest_ShellTransit.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app from the recents app view (the overview)
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
+ *
+ * Actions:
+ *     Launch [testApp]
+ *     Press recents
+ *     Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
+ *     complete (only this action is traced)
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [OpenAppTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@FlakyTest(bugId = 219688533)
+class OpenAppFromOverviewTest_ShellTransit(testSpec: FlickerTestParameter)
+    : OpenAppFromOverviewTest(testSpec) {
+    @Before
+    override fun before() {
+        assumeTrue(isShellTransitionsEnabled)
+    }
+
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 216266712)
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+            super.appWindowReplacesLauncherAsTopWindow()
+
+    /** {@inheritDoc} */
+    @FlakyTest(bugId = 218470989)
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index f75c50e..b0e53e9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,19 +16,22 @@
 
 package com.android.server.wm.flicker.launch
 
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -57,11 +60,16 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
-class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
+open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
     : OpenAppFromLockTransition(testSpec) {
     override val testApp = NonResizeableAppHelper(instrumentation)
     private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
 
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Checks that the nav bar layer starts invisible, becomes visible during unlocking animation
      * and remains visible at the end
@@ -148,6 +156,11 @@
     @Test
     override fun entireScreenCovered() = super.entireScreenCovered()
 
+    @FlakyTest(bugId = 218470989)
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt
new file mode 100644
index 0000000..8a08d07
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest_ShellTransit.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app while the device is locked
+ *
+ * To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
+ *
+ * Actions:
+ *     Lock the device.
+ *     Launch an app on top of the lock screen [testApp] and wait animation to complete
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [OpenAppTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@FlakyTest(bugId = 219688533)
+class OpenAppNonResizeableTest_ShellTransit(testSpec: FlickerTestParameter)
+    : OpenAppNonResizeableTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index d2f6c7f1..4313b8d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import android.app.Instrumentation
-import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.FlakyTest
 import android.platform.test.annotations.Presubmit
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -56,7 +56,7 @@
         }
         teardown {
             test {
-                testApp.exit()
+                testApp.exit(wmHelper)
             }
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 3159bf1..2562098 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -17,14 +17,17 @@
 package com.android.server.wm.flicker.launch
 
 import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
-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.flicker.helpers.setRotation
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,8 +57,13 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
-class OpenAppWarmTest(testSpec: FlickerTestParameter)
+open class OpenAppWarmTest(testSpec: FlickerTestParameter)
     : OpenAppFromLauncherTransition(testSpec) {
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Defines the transition used to run the test
      */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt
new file mode 100644
index 0000000..3958dd2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest_ShellTransit.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test warm launching an app from launcher
+ *
+ * To run this test: `atest FlickerTests:OpenAppWarmTest`
+ *
+ * Actions:
+ *     Launch [testApp]
+ *     Press home
+ *     Relaunch an app [testApp] and wait animation to complete (only this action is traced)
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [OpenAppTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@FlakyTest(bugId = 219688533)
+class OpenAppWarmTest_ShellTransit(testSpec: FlickerTestParameter)
+    : OpenAppWarmTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 5301e02..f21b1d6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -33,12 +33,15 @@
 import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,7 +64,7 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group1
-class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
+open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
 
     private val testApp1 = SimpleAppHelper(instrumentation)
@@ -69,6 +72,11 @@
 
     private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
 
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
@@ -326,4 +334,4 @@
                     )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
new file mode 100644
index 0000000..cffed81
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching back to previous app from last opened app
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ *
+ * Actions:
+ *     Launch an app [testApp1]
+ *     Launch another app [testApp2]
+ *     Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@FlakyTest(bugId = 219690120)
+class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(testSpec: FlickerTestParameter)
+    : QuickSwitchBetweenTwoAppsBackTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f603f6e..2b944c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -112,10 +112,7 @@
      * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
      * doesn't flicker, and disappears before the transition is complete
      */
-    @Presubmit
-    @Test
-    fun rotationLayerAppearsAndVanishes() {
-        Assume.assumeFalse(isShellTransitionsEnabled)
+    fun rotationLayerAppearsAndVanishesAssertion() {
         testSpec.assertLayers {
             this.isVisible(testApp.component)
                 .then()
@@ -126,11 +123,26 @@
         }
     }
 
+    /**
+     * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+     * doesn't flicker, and disappears before the transition is complete
+     */
+    @Presubmit
+    @Test
+    fun rotationLayerAppearsAndVanishes() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        rotationLayerAppearsAndVanishesAssertion()
+    }
+
+    /**
+     * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+     * doesn't flicker, and disappears before the transition is complete
+     */
     @FlakyTest(bugId = 218484127)
     @Test
     fun rotationLayerAppearsAndVanishes_shellTransit() {
         Assume.assumeTrue(isShellTransitionsEnabled)
-        rotationLayerAppearsAndVanishes()
+        rotationLayerAppearsAndVanishesAssertion()
     }
 
     /**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index d1bdeed..0becadf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -153,4 +153,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 3ae484b..15fd5e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,17 +17,20 @@
 package com.android.server.wm.flicker.rotation
 
 import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
 import android.view.WindowManager
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,11 +79,16 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
-class SeamlessAppRotationTest(
+open class SeamlessAppRotationTest(
     testSpec: FlickerTestParameter
 ) : RotationTransition(testSpec) {
     override val testApp = SeamlessRotationAppHelper(instrumentation)
 
+    @Before
+    open fun before() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+    }
+
     override val transition: FlickerBuilder.() -> Unit
         get() = {
             super.transition(this)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt
new file mode 100644
index 0000000..d397d59
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest_ShellTransit.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.rotation
+
+import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test opening an app and cycling through app rotations using seamless rotations
+ *
+ * Currently runs:
+ *      0 -> 90 degrees
+ *      0 -> 90 degrees (with starved UI thread)
+ *      90 -> 0 degrees
+ *      90 -> 0 degrees (with starved UI thread)
+ *
+ * Actions:
+ *     Launch an app in fullscreen and supporting seamless rotation (via intent)
+ *     Set initial device orientation
+ *     Start tracing
+ *     Change device orientation
+ *     Stop tracing
+ *
+ * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [RotationTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+@FlakyTest(bugId = 219689723)
+class SeamlessAppRotationTest_ShellTransit(
+    testSpec: FlickerTestParameter
+) : SeamlessAppRotationTest(testSpec) {
+    @Before
+    override fun before() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+    }
+}
diff --git a/tests/Internal/src/com/android/internal/util/UserIconsTest.java b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
new file mode 100644
index 0000000..cc7b20b
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UserIconsTest {
+
+    @Test
+    public void convertToBitmapAtUserIconSize_sizeIsCorrect() {
+        Resources res = InstrumentationRegistry.getTargetContext().getResources();
+        Drawable icon = UserIcons.getDefaultUserIcon(res, 0, true);
+        Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, icon);
+        int expectedSize = res.getDimensionPixelSize(R.dimen.user_icon_size);
+
+        assertThat(bitmap.getWidth()).isEqualTo(expectedSize);
+        assertThat(bitmap.getHeight()).isEqualTo(expectedSize);
+    }
+
+}
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index c59a41e..9a9e42b 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,8 +37,6 @@
         "vts",
     ],
     data: [
-        ":NotoSerif-Regular.ttf",
-        ":NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTestCertDer",
         ":UpdatableSystemFontTest_NotoColorEmoji.ttf",
         ":UpdatableSystemFontTest_NotoColorEmoji.sig",
@@ -48,7 +46,9 @@
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Regular.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
     ],
     sdk_version: "test_current",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 6effa7b..9e2a4b6 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -28,11 +28,7 @@
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
-        <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
-        <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
-        <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
@@ -40,6 +36,10 @@
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 87fda0d..cbe13d9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -84,7 +84,7 @@
 
     private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
     private static final String NOTO_COLOR_EMOJI_TTF =
-            "/data/local/tmp/NotoColorEmoji.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf";
     private static final String NOTO_COLOR_EMOJI_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
     // A font with revision == 0.
@@ -105,13 +105,13 @@
 
     private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
     private static final String NOTO_SERIF_REGULAR_TTF =
-            "/data/local/tmp/NotoSerif-Regular.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf";
     private static final String NOTO_SERIF_REGULAR_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
 
     private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
     private static final String NOTO_SERIF_BOLD_TTF =
-            "/data/local/tmp/NotoSerif-Bold.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf";
     private static final String NOTO_SERIF_BOLD_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
 
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 64b698d..0bdb3a8 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -21,11 +21,19 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font file name.
 filegroup {
     name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
-    srcs: ["NotoColorEmoji.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
 }
 
 filegroup {
@@ -43,10 +51,6 @@
     srcs: ["UpdatableSystemFontTestCert.der"],
 }
 
-genrule_defaults {
-    name: "updatable_system_font_increment_font_revision_default",
-}
-
 genrule {
     name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
     srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
@@ -124,13 +128,13 @@
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Regular.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
 }
 
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Bold.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
 }
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
new file mode 100644
index 0000000..66c1bd2
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
new file mode 100644
index 0000000..8c4215e
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
@@ -0,0 +1,196 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Bold.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Bold.ttf, the PostScript needs to be
+           the same as NotoSerif-Bold.ttf here. -->
+      NotoSerif-Bold
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
new file mode 100644
index 0000000..707ae28
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
new file mode 100644
index 0000000..754eae3
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
@@ -0,0 +1,196 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Regular.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Regular.ttf, the PostScript needs to be
+           the same as NotoSerif-Regular.ttf here. -->
+      NotoSerif
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+      See the License for the specific language governing permissions and
+      limitations under the License.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 978bf3e..7b1f7a5 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -174,7 +174,7 @@
 
     private IntentFilter getIntentFilter() {
         final ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class);
-        verify(mContext).registerReceiver(any(), captor.capture(), any(), any());
+        verify(mContext).registerReceiver(any(), captor.capture(), any(), any(), anyInt());
 
         return captor.getValue();
     }
@@ -258,7 +258,8 @@
                         eq(mTelephonySubscriptionTracker),
                         any(IntentFilter.class),
                         any(),
-                        eq(mHandler));
+                        eq(mHandler),
+                        eq(Context.RECEIVER_NOT_EXPORTED));
         final IntentFilter filter = getIntentFilter();
         assertEquals(2, filter.countActions());
         assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e547400..4cfa93b 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -307,7 +307,10 @@
                         ncCaptor.capture(),
                         lpCaptor.capture(),
                         any(),
-                        argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE),
+                        // Subtype integer/name and extras do not have getters; cannot be tested.
+                        argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE
+                                && nac.getLegacyTypeName().equals(
+                                        VcnGatewayConnection.NETWORK_INFO_NETWORK_TYPE_STRING)),
                         any(),
                         any(),
                         any());
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..52c5d48 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -36,6 +36,7 @@
 
 cc_defaults {
     name: "aapt2_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 7103944..f47d66e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -80,7 +80,7 @@
           printer_->Print(parent_name.package);
           printer_->Print(":");
         }
-        printer_->Print(to_string(parent_name.type));
+        printer_->Print(parent_name.type.to_string());
         printer_->Print("/");
         printer_->Print(parent_name.entry);
         if (parent_ref.id) {
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 6364ccd..0bb330e 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -134,6 +134,24 @@
     {"xml", ResourceType::kXml},
 };
 
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t) {
+  return {to_string(t), t};
+}
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) {
+  auto colon = std::find(s.begin(), s.end(), ':');
+  const ResourceType* parsedType;
+  if (colon != s.end() && colon != std::prev(s.end())) {
+    parsedType = ParseResourceType(s.substr(s.begin(), colon));
+  } else {
+    parsedType = ParseResourceType(s);
+  }
+  if (parsedType == nullptr) {
+    return std::nullopt;
+  }
+  return ResourceNamedTypeRef(s, *parsedType);
+}
+
 const ResourceType* ParseResourceType(const StringPiece& str) {
   auto iter = sResourceTypeMap.find(str);
   if (iter == std::end(sResourceTypeMap)) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 307c21d..b41d851 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -19,22 +19,21 @@
 
 #include <iomanip>
 #include <limits>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <tuple>
 #include <vector>
 
+#include "Source.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/StringPiece.h"
 #include "utils/JenkinsHash.h"
 
-#include "Source.h"
-
 namespace aapt {
 
 /**
- * The various types of resource types available. Corresponds
- * to the 'type' in package:type/entry.
+ * The various types of resource types available.
  */
 enum class ResourceType {
   kAnim,
@@ -78,15 +77,63 @@
 const ResourceType* ParseResourceType(const android::StringPiece& str);
 
 /**
+ * Pair of type name as in ResourceTable and actual resource type.
+ * Corresponds to the 'type' in package:type/entry.
+ *
+ * This is to support resource types with custom names inside resource tables.
+ */
+struct ResourceNamedType {
+  std::string name;
+  ResourceType type = ResourceType::kRaw;
+
+  ResourceNamedType() = default;
+  ResourceNamedType(const android::StringPiece& n, ResourceType t);
+
+  int compare(const ResourceNamedType& other) const;
+
+  const std::string& to_string() const;
+};
+
+/**
+ * Same as ResourceNamedType, but uses StringPieces instead.
+ * Use this if you need to avoid copying and know that
+ * the lifetime of this object is shorter than that
+ * of the original string.
+ */
+struct ResourceNamedTypeRef {
+  android::StringPiece name;
+  ResourceType type = ResourceType::kRaw;
+
+  ResourceNamedTypeRef() = default;
+  ResourceNamedTypeRef(const ResourceNamedTypeRef&) = default;
+  ResourceNamedTypeRef(ResourceNamedTypeRef&&) = default;
+  ResourceNamedTypeRef(const ResourceNamedType& rhs);  // NOLINT(google-explicit-constructor)
+  ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t);
+  ResourceNamedTypeRef& operator=(const ResourceNamedTypeRef& rhs) = default;
+  ResourceNamedTypeRef& operator=(ResourceNamedTypeRef&& rhs) = default;
+  ResourceNamedTypeRef& operator=(const ResourceNamedType& rhs);
+
+  ResourceNamedType ToResourceNamedType() const;
+
+  std::string to_string() const;
+};
+
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t);
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s);
+
+/**
  * A resource's name. This can uniquely identify
  * a resource in the ResourceTable.
  */
 struct ResourceName {
   std::string package;
-  ResourceType type = ResourceType::kRaw;
+  ResourceNamedType type;
   std::string entry;
 
   ResourceName() = default;
+  ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+               const android::StringPiece& e);
   ResourceName(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
 
   int compare(const ResourceName& other) const;
@@ -103,13 +150,15 @@
  */
 struct ResourceNameRef {
   android::StringPiece package;
-  ResourceType type = ResourceType::kRaw;
+  ResourceNamedTypeRef type;
   android::StringPiece entry;
 
   ResourceNameRef() = default;
   ResourceNameRef(const ResourceNameRef&) = default;
   ResourceNameRef(ResourceNameRef&&) = default;
   ResourceNameRef(const ResourceName& rhs);  // NOLINT(google-explicit-constructor)
+  ResourceNameRef(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+                  const android::StringPiece& e);
   ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
   ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
   ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -295,17 +344,98 @@
 }
 
 //
+// ResourceNamedType implementation.
+//
+inline ResourceNamedType::ResourceNamedType(const android::StringPiece& n, ResourceType t)
+    : name(n.to_string()), type(t) {
+}
+
+inline int ResourceNamedType::compare(const ResourceNamedType& other) const {
+  int cmp = static_cast<int>(type) - static_cast<int>(other.type);
+  if (cmp != 0) return cmp;
+  cmp = name.compare(other.name);
+  return cmp;
+}
+
+inline const std::string& ResourceNamedType::to_string() const {
+  return name;
+}
+
+inline bool operator<(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) < 0;
+}
+
+inline bool operator==(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) == 0;
+}
+
+inline bool operator!=(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) != 0;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedType& val) {
+  return out << val.to_string();
+}
+
+//
+// ResourceNamedTypeRef implementation.
+//
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t)
+    : name(n), type(t) {
+}
+
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const ResourceNamedType& rhs)
+    : name(rhs.name), type(rhs.type) {
+}
+
+inline ResourceNamedTypeRef& ResourceNamedTypeRef::operator=(const ResourceNamedType& rhs) {
+  name = rhs.name;
+  type = rhs.type;
+  return *this;
+}
+
+inline ResourceNamedType ResourceNamedTypeRef::ToResourceNamedType() const {
+  return ResourceNamedType(name, type);
+}
+
+inline std::string ResourceNamedTypeRef::to_string() const {
+  return name.to_string();
+}
+
+inline bool operator<(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) < std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator==(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) == std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator!=(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) != std::tie(rhs.type, rhs.name);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedTypeRef& val) {
+  return out << val.name;
+}
+
+//
 // ResourceName implementation.
 //
 
+inline ResourceName::ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+                                  const android::StringPiece& e)
+    : package(p.to_string()), type(t.ToResourceNamedType()), entry(e.to_string()) {
+}
+
 inline ResourceName::ResourceName(const android::StringPiece& p, ResourceType t,
                                   const android::StringPiece& e)
-    : package(p.to_string()), type(t), entry(e.to_string()) {}
+    : ResourceName(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
 
 inline int ResourceName::compare(const ResourceName& other) const {
   int cmp = package.compare(other.package);
   if (cmp != 0) return cmp;
-  cmp = static_cast<int>(type) - static_cast<int>(other.type);
+  cmp = type.compare(other.type);
   if (cmp != 0) return cmp;
   cmp = entry.compare(other.entry);
   return cmp;
@@ -341,9 +471,16 @@
 inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
     : package(rhs.package), type(rhs.type), entry(rhs.entry) {}
 
+inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p,
+                                        const ResourceNamedTypeRef& t,
+                                        const android::StringPiece& e)
+    : package(p), type(t), entry(e) {
+}
+
 inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p, ResourceType t,
                                         const android::StringPiece& e)
-    : package(p), type(t), entry(e) {}
+    : ResourceNameRef(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
 
 inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
   package = rhs.package;
@@ -400,7 +537,7 @@
   size_t operator()(const aapt::ResourceName& name) const {
     android::hash_t h = 0;
     h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
-    h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+    h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.type.name)));
     h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
     return static_cast<size_t>(h);
   }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 792a306..42715f9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -604,7 +604,8 @@
       return false;
     }
 
-    out_resource->name.type = ResourceType::kId;
+    out_resource->name.type =
+        ResourceNamedTypeWithDefaultName(ResourceType::kId).ToResourceNamedType();
     out_resource->name.entry = maybe_name.value().to_string();
 
     // Ids either represent a unique resource id or reference another resource id
@@ -623,7 +624,7 @@
         // A null reference also means there is no inner element when ids are in the form:
         //    <id name="name"/>
         out_resource->value = util::make_unique<Id>();
-      } else if (!ref || ref->name.value().type != ResourceType::kId) {
+      } else if (!ref || ref->name.value().type.type != ResourceType::kId) {
         // If an inner element exists, the inner element must be a reference to another resource id
         diag_->Error(DiagMessage(out_resource->source)
                          << "<" << parser->element_name()
@@ -640,7 +641,8 @@
       return false;
     }
 
-    out_resource->name.type = ResourceType::kMacro;
+    out_resource->name.type =
+        ResourceNamedTypeWithDefaultName(ResourceType::kMacro).ToResourceNamedType();
     out_resource->name.entry = maybe_name.value().to_string();
     return ParseMacro(parser, out_resource);
   }
@@ -656,7 +658,8 @@
         return false;
       }
 
-      out_resource->name.type = item_iter->second.type;
+      out_resource->name.type =
+          ResourceNamedTypeWithDefaultName(item_iter->second.type).ToResourceNamedType();
       out_resource->name.entry = maybe_name.value().to_string();
 
       // Only use the implied format of the type when there is no explicit format.
@@ -699,7 +702,7 @@
   if (can_be_item) {
     // Try parsing the elementName (or type) as a resource. These shall only be
     // resources like 'layout' or 'xml' and they can only be references.
-    const ResourceType* parsed_type = ParseResourceType(resource_type);
+    std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(resource_type);
     if (parsed_type) {
       if (!maybe_name) {
         diag_->Error(DiagMessage(out_resource->source)
@@ -708,7 +711,7 @@
         return false;
       }
 
-      out_resource->name.type = *parsed_type;
+      out_resource->name.type = parsed_type->ToResourceNamedType();
       out_resource->name.entry = maybe_name.value().to_string();
       out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
       if (!out_resource->value) {
@@ -933,7 +936,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
                                                    << maybe_type.value()
@@ -941,7 +944,7 @@
     return false;
   }
 
-  out_resource->name.type = *parsed_type;
+  out_resource->name.type = parsed_type->ToResourceNamedType();
 
   if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
     std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
@@ -953,7 +956,7 @@
     out_resource->id = maybe_id.value();
   }
 
-  if (*parsed_type == ResourceType::kId) {
+  if (parsed_type->type == ResourceType::kId) {
     // An ID marked as public is also the definition of an ID.
     out_resource->value = util::make_unique<Id>();
   }
@@ -978,7 +981,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag->Error(DiagMessage(out_resource->source)
                 << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
@@ -1096,7 +1099,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag_->Error(DiagMessage(out_resource->source)
                  << "invalid resource type '" << maybe_type.value() << "' in <"
@@ -1104,7 +1107,7 @@
     return false;
   }
 
-  out_resource->name.type = *parsed_type;
+  out_resource->name.type = parsed_type->ToResourceNamedType();
   return true;
 }
 
@@ -1208,8 +1211,8 @@
         continue;
       }
 
-      const ResourceType* type = ParseResourceType(item_type.value());
-      if (type == nullptr) {
+      std::optional<ResourceNamedTypeRef> type = ParseResourceNamedType(item_type.value());
+      if (!type) {
         diag_->Error(DiagMessage(element_source)
                      << "invalid resource type '" << item_type.value()
                      << "' in <item> within an <overlayable>");
@@ -1223,7 +1226,7 @@
       overlayable_item.source = element_source;
 
       ParsedResource child_resource{};
-      child_resource.name.type = *type;
+      child_resource.name.type = type->ToResourceNamedType();
       child_resource.name.entry = item_name.value().to_string();
       child_resource.overlayable_item = overlayable_item;
       out_resource->child_resources.push_back(std::move(child_resource));
@@ -1289,7 +1292,8 @@
 
 bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
                                    ParsedResource* out_resource, bool weak) {
-  out_resource->name.type = ResourceType::kAttr;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kAttr).ToResourceNamedType();
 
   // Attributes only end up in default configuration.
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
@@ -1475,7 +1479,8 @@
   }
 
   return Attribute::Symbol{
-      Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
+      Reference(ResourceNameRef({}, ResourceNamedTypeWithDefaultName(ResourceType::kId),
+                                maybe_name.value())),
       val.data, val.dataType};
 }
 
@@ -1509,7 +1514,7 @@
 
 bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
                                 ParsedResource* out_resource) {
-  out_resource->name.type = type;
+  out_resource->name.type = ResourceNamedTypeWithDefaultName(type).ToResourceNamedType();
 
   std::unique_ptr<Style> style = util::make_unique<Style>();
 
@@ -1535,7 +1540,8 @@
     size_t pos = style_name.find_last_of(u'.');
     if (pos != std::string::npos) {
       style->parent_inferred = true;
-      style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+      style->parent = Reference(ResourceName(
+          {}, ResourceNamedTypeWithDefaultName(ResourceType::kStyle), style_name.substr(0, pos)));
     }
   }
 
@@ -1591,7 +1597,8 @@
 bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
                                     ParsedResource* out_resource,
                                     const uint32_t typeMask) {
-  out_resource->name.type = ResourceType::kArray;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kArray).ToResourceNamedType();
 
   std::unique_ptr<Array> array = util::make_unique<Array>();
 
@@ -1646,7 +1653,8 @@
 
 bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
                                  ParsedResource* out_resource) {
-  out_resource->name.type = ResourceType::kPlurals;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kPlurals).ToResourceNamedType();
 
   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
 
@@ -1727,7 +1735,8 @@
 
 bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
                                            ParsedResource* out_resource) {
-  out_resource->name.type = ResourceType::kStyleable;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kStyleable).ToResourceNamedType();
 
   if (!options_.preserve_visibility_of_styleables) {
     // This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index dae89b0..98cce26 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -473,7 +473,7 @@
   }
 
   auto package = FindOrCreatePackage(res.name.package);
-  auto type = package->FindOrCreateType(res.name.type);
+  auto type = package->FindOrCreateType(res.name.type.type);
   auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
                                    NameEqualRange<ResourceEntry>{});
   const size_t entry_count = std::distance(entry_it.first, entry_it.second);
@@ -593,7 +593,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
@@ -612,7 +612,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
@@ -633,7 +633,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ead06bf..23f6c88 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -50,12 +50,11 @@
   name_out.package =
       util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
 
-  const ResourceType* type;
+  std::optional<ResourceNamedTypeRef> type;
   if (name_in.type) {
-    type = ParseResourceType(
-        util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
+    type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
   } else if (name_in.type8) {
-    type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
+    type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen));
   } else {
     return {};
   }
@@ -64,7 +63,7 @@
     return {};
   }
 
-  name_out.type = *type;
+  name_out.type = type->ToResourceNamedType();
 
   if (name_in.name) {
     name_out.entry =
@@ -85,12 +84,12 @@
 
   name_out.package = std::string(name_in.package, name_in.package_len);
 
-  const ResourceType* type;
+  std::optional<ResourceNamedTypeRef> type;
   if (name_in.type16) {
-    type = ParseResourceType(
-        util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+    type =
+        ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
   } else if (name_in.type) {
-    type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+    type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len));
   } else {
     return {};
   }
@@ -99,7 +98,7 @@
     return {};
   }
 
-  name_out.type = *type;
+  name_out.type = type->ToResourceNamedType();
 
   if (name_in.entry16) {
     name_out.entry =
@@ -133,7 +132,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(type);
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type);
   if (!parsed_type) {
     return false;
   }
@@ -181,7 +180,7 @@
       return false;
     }
 
-    if (create && name.type != ResourceType::kId) {
+    if (create && name.type.type != ResourceType::kId) {
       return false;
     }
 
@@ -230,7 +229,7 @@
 
     if (out_ref) {
       out_ref->package = package;
-      out_ref->type = ResourceType::kAttr;
+      out_ref->type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
       out_ref->entry = entry;
     }
     return true;
@@ -272,7 +271,7 @@
   }
 
   ResourceNameRef ref;
-  ref.type = ResourceType::kStyle;
+  ref.type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
 
   StringPiece type_str;
   android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
@@ -323,7 +322,8 @@
     p++;
   }
 
-  ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
+  ref.name = ResourceName(package, ResourceNamedTypeWithDefaultName(ResourceType::kAttr),
+                          name.empty() ? trimmed_str : name);
   return std::optional<Reference>(std::move(ref));
 }
 
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b3ab4ff..b796eb0 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -116,7 +116,7 @@
 }
 
 bool Reference::Flatten(android::Res_value* out_value) const {
-  if (name && name.value().type == ResourceType::kMacro) {
+  if (name && name.value().type.type == ResourceType::kMacro) {
     return false;
   }
 
@@ -192,7 +192,7 @@
     if (print_package) {
       printer->Print(name.to_string());
     } else {
-      printer->Print(to_string(name.type));
+      printer->Print(name.type.to_string());
       printer->Print("/");
       printer->Print(name.entry);
     }
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index c557f3c..2c55d1d 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -18,6 +18,9 @@
 
 #include "test/Test.h"
 
+using ::testing::Eq;
+using ::testing::Optional;
+
 namespace aapt {
 
 TEST(ResourceTypeTest, ParseResourceTypes) {
@@ -125,4 +128,104 @@
   EXPECT_EQ(type, nullptr);
 }
 
+TEST(ResourceTypeTest, ParseResourceNamedType) {
+  auto type = ParseResourceNamedType("anim");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("anim", ResourceType::kAnim))));
+
+  type = ParseResourceNamedType("layout");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:2");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:another");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:");
+  EXPECT_THAT(type, Eq(std::nullopt));
+
+  type = ParseResourceNamedType("layout2");
+  EXPECT_THAT(type, Eq(std::nullopt));
+
+  type = ParseResourceNamedType("blahaha");
+  EXPECT_THAT(type, Eq(std::nullopt));
+}
+
+TEST(ResourceTypeTest, ResourceNamedTypeWithDefaultName) {
+  auto type = ResourceNamedTypeWithDefaultName(ResourceType::kAnim);
+  EXPECT_THAT(type, Eq(ResourceNamedType("anim", ResourceType::kAnim)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAnimator);
+  EXPECT_THAT(type, Eq(ResourceNamedType("animator", ResourceType::kAnimator)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kArray);
+  EXPECT_THAT(type, Eq(ResourceNamedType("array", ResourceType::kArray)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
+  EXPECT_THAT(type, Eq(ResourceNamedType("attr", ResourceType::kAttr)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate);
+  EXPECT_THAT(type, Eq(ResourceNamedType("^attr-private", ResourceType::kAttrPrivate)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kBool);
+  EXPECT_THAT(type, Eq(ResourceNamedType("bool", ResourceType::kBool)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kColor);
+  EXPECT_THAT(type, Eq(ResourceNamedType("color", ResourceType::kColor)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kConfigVarying);
+  EXPECT_THAT(type, Eq(ResourceNamedType("configVarying", ResourceType::kConfigVarying)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kDimen);
+  EXPECT_THAT(type, Eq(ResourceNamedType("dimen", ResourceType::kDimen)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kDrawable);
+  EXPECT_THAT(type, Eq(ResourceNamedType("drawable", ResourceType::kDrawable)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kFont);
+  EXPECT_THAT(type, Eq(ResourceNamedType("font", ResourceType::kFont)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kFraction);
+  EXPECT_THAT(type, Eq(ResourceNamedType("fraction", ResourceType::kFraction)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kId);
+  EXPECT_THAT(type, Eq(ResourceNamedType("id", ResourceType::kId)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kInteger);
+  EXPECT_THAT(type, Eq(ResourceNamedType("integer", ResourceType::kInteger)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kInterpolator);
+  EXPECT_THAT(type, Eq(ResourceNamedType("interpolator", ResourceType::kInterpolator)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kLayout);
+  EXPECT_THAT(type, Eq(ResourceNamedType("layout", ResourceType::kLayout)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kMenu);
+  EXPECT_THAT(type, Eq(ResourceNamedType("menu", ResourceType::kMenu)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kMipmap);
+  EXPECT_THAT(type, Eq(ResourceNamedType("mipmap", ResourceType::kMipmap)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kNavigation);
+  EXPECT_THAT(type, Eq(ResourceNamedType("navigation", ResourceType::kNavigation)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kPlurals);
+  EXPECT_THAT(type, Eq(ResourceNamedType("plurals", ResourceType::kPlurals)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kRaw);
+  EXPECT_THAT(type, Eq(ResourceNamedType("raw", ResourceType::kRaw)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kString);
+  EXPECT_THAT(type, Eq(ResourceNamedType("string", ResourceType::kString)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
+  EXPECT_THAT(type, Eq(ResourceNamedType("style", ResourceType::kStyle)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kTransition);
+  EXPECT_THAT(type, Eq(ResourceNamedType("transition", ResourceType::kTransition)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kXml);
+  EXPECT_THAT(type, Eq(ResourceNamedType("xml", ResourceType::kXml)));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e614a75..1efe6c2 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -207,7 +207,7 @@
     }
 
     // Check to see if this is an 'id' with the target package.
-    if (name.type == ResourceType::kId && symbol->id) {
+    if (name.type.type == ResourceType::kId && symbol->id) {
       ResourceId* id = &symbol->id.value();
       if (id->package_id() > kAppPackageId) {
         // Rewrite the resource ID to be compatible pre-O.
@@ -1057,6 +1057,83 @@
     return true;
   }
 
+  bool VerifyLocaleFormat(xml::XmlResource* manifest, IDiagnostics* diag) {
+    // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application>
+    // element.
+    const xml::Element* application = manifest->root->FindChild("", "application");
+    if (!application) {
+      return true;
+    }
+    const xml::Attribute* localeConfig =
+        application->FindAttribute(xml::kSchemaAndroid, "localeConfig");
+    if (!localeConfig) {
+      return true;
+    }
+
+    if (localeConfig->compiled_value) {
+      const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get());
+      const auto localeconfig_entry =
+          ResolveTableEntry(context_, &final_table_, localeconfig_reference);
+      if (!localeconfig_entry) {
+        return true;
+      }
+
+      for (const auto& value : localeconfig_entry->values) {
+        // Load an XML file which is linked from the localeConfig attribute.
+        const std::string& path = value->value->GetSource().path;
+        std::unique_ptr<xml::XmlResource> localeConfig_xml = LoadXml(path, diag);
+        if (!localeConfig_xml) {
+          diag->Error(DiagMessage(path) << "can't load the XML");
+          return false;
+        }
+
+        xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get());
+        if (!localeConfig_el) {
+          diag->Error(DiagMessage(path) << "no root tag defined");
+          return false;
+        }
+        if (localeConfig_el->name != "locale-config") {
+          diag->Error(DiagMessage(path) << "invalid element name: " << localeConfig_el->name
+                                        << ", expected: locale-config");
+          return false;
+        }
+
+        for (const xml::Element* child_el : localeConfig_el->GetChildElements()) {
+          if (child_el->name == "locale") {
+            if (const xml::Attribute* locale_name_attr =
+                    child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
+              const std::string& locale_name = locale_name_attr->value;
+              const std::string valid_name = ConvertToBCP47Tag(locale_name);
+
+              // Start to verify the locale format
+              ConfigDescription config;
+              if (!ConfigDescription::Parse(valid_name, &config)) {
+                diag->Error(DiagMessage(path) << "invalid configuration: " << locale_name);
+                return false;
+              }
+            } else {
+              diag->Error(DiagMessage(path) << "the attribute android:name is not found");
+              return false;
+            }
+          } else {
+            diag->Error(DiagMessage(path)
+                        << "invalid element name: " << child_el->name << ", expected: locale");
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+  std::string ConvertToBCP47Tag(const std::string& locale) {
+    std::string bcp47tag = "b+";
+    bcp47tag += locale;
+    std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+');
+
+    return bcp47tag;
+  }
+
   std::unique_ptr<IArchiveWriter> MakeArchiveWriter(const StringPiece& out) {
     if (options_.output_to_directory) {
       return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out);
@@ -2180,6 +2257,10 @@
       return 1;
     }
 
+    if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) {
+      return 1;
+    };
+
     if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
       return 1;
     }
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 430c184..7b1236a 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -22,6 +22,7 @@
 #include "LoadedApk.h"
 #include "test/Test.h"
 
+using android::ConfigDescription;
 using testing::Eq;
 using testing::HasSubstr;
 using testing::IsNull;
@@ -783,4 +784,51 @@
   EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
 }
 
+TEST_F(LinkTest, ParseLocaleConfig) {
+  StdErrDiagnostics diag;
+  const std::string xml_values =
+      R"(<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
+            <locale android:name="pt"/>
+            <locale android:name="chr"/>
+            <locale android:name="chr-US"/>
+            <locale android:name="zh-Hant"/>
+            <locale android:name="es-419"/>
+            <locale android:name="en-US"/>
+            <locale android:name="zh-Hans-SG"/>
+        </locale-config>)";
+
+  const std::string res = GetTestPath("test-res");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/locale_config.xml"), xml_values, res, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  auto link_args = LinkCommandBuilder(this)
+                       .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+                       .AddCompiledResDir(res, &diag)
+                       .AddFlag("--no-auto-version")
+                       .Build(out_apk);
+  ASSERT_TRUE(Link(link_args, &diag));
+
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  ASSERT_THAT(apk, Ne(nullptr));
+
+  auto xml = apk->LoadXml("res/xml/locale_config.xml", &diag);
+  ASSERT_THAT(xml, NotNull());
+  EXPECT_THAT(xml->root->name, Eq("locale-config"));
+  ASSERT_THAT(xml->root->children.size(), Eq(7));
+  for (auto& node : xml->root->children) {
+    const xml::Element* child_el = xml::NodeCast<xml::Element>(node.get());
+    ASSERT_THAT(child_el, NotNull());
+    EXPECT_THAT(child_el->name, Eq("locale"));
+
+    auto& xml_attrs = child_el->attributes;
+    for (auto& attr : xml_attrs) {
+      std::string locale = "b+";
+      locale += attr.value;
+      std::replace(locale.begin(), locale.end(), '-', '+');
+      ConfigDescription config;
+      ASSERT_TRUE(ConfigDescription::Parse(locale, &config));
+    }
+  }
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 339b8af..fa816be 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -275,7 +275,7 @@
     return false;
   }
 
-  auto key = ResourceTypeKey{name.type, id.type_id()};
+  auto key = ResourceTypeKey{name.type.type, id.type_id()};
   auto type = types_.find(key);
   if (type == types_.end()) {
     // The type has not been assigned an id yet. Ensure that the specified id is not being used by
@@ -291,7 +291,7 @@
 
   if (!visibility.staged_api) {
     // Ensure that non-staged resources can only exist in one type ID.
-    auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+    auto non_staged_type = non_staged_type_ids_.emplace(name.type.type, id.type_id());
     if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
       diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
                                 << " because type already has ID " << std::hex
@@ -316,14 +316,14 @@
   CHECK(name.package.empty() || name.package == package_name_);
 
   // Find the type id for non-staged resources of this type.
-  auto non_staged_type = non_staged_type_ids_.find(name.type);
+  auto non_staged_type = non_staged_type_ids_.find(name.type.type);
   if (non_staged_type == non_staged_type_ids_.end()) {
     auto next_type_id = type_id_finder_.NextId();
     CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
-    non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+    non_staged_type = non_staged_type_ids_.emplace(name.type.type, *next_type_id).first;
   }
 
-  ResourceTypeKey key{name.type, non_staged_type->second};
+  ResourceTypeKey key{name.type.type, non_staged_type->second};
   auto type = types_.find(key);
   if (type == types_.end()) {
     type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 5054115..bb72159 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -46,7 +46,7 @@
       ResourceNameRef name;
       bool create = false;
       if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
-        if (create && name.type == ResourceType::kId) {
+        if (create && name.type.type == ResourceType::kId) {
           if (!text::IsValidResourceEntryName(name.entry)) {
             source_diag_->Error(DiagMessage(element->line_number)
                                    << "id '" << name << "' has an invalid entry name");
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 11d02f0..c65c550 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -556,8 +556,8 @@
 std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
                                                        const ConfigDescription& config,
                                                        const android::Res_value& value) {
-  std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_,
-                                                                  value, &table_->string_pool);
+  std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(
+      name.type.type, config, value_pool_, value, &table_->string_pool);
   if (files_ != nullptr) {
     FileReference* file_ref = ValueCast<FileReference>(item.get());
     if (file_ref != nullptr) {
@@ -575,7 +575,7 @@
 std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
                                                            const ConfigDescription& config,
                                                            const ResTable_map_entry* map) {
-  switch (name.type) {
+  switch (name.type.type) {
     case ResourceType::kStyle:
       // fallthrough
     case ResourceType::kConfigVarying:  // legacy thing used in tests
@@ -594,8 +594,8 @@
       // We can ignore the value here.
       return util::make_unique<Id>();
     default:
-      diag_->Error(DiagMessage() << "illegal map type '" << to_string(name.type) << "' ("
-                                 << (int)name.type << ")");
+      diag_->Error(DiagMessage() << "illegal map type '" << name.type << "' ("
+                                 << (int)name.type.type << ")");
       break;
   }
   return {};
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 3b3c6e1..a963d98 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -229,8 +229,8 @@
   const std::string package_name =
       name.package.empty() ? fallback_package_name.to_string() : name.package;
   const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
-  return FieldReference(
-      StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+  return FieldReference(StringPrintf("%s.R.%s.%s", package_name.c_str(),
+                                     name.type.to_string().data(), entry.c_str()));
 }
 
 bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
@@ -451,7 +451,7 @@
                                          MethodDefinition* out_rewrite_method,
                                          text::Printer* r_txt_printer) {
   ResourceId real_id = id;
-  if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
+  if (context_->GetMinSdkVersion() < SDK_O && name.type.type == ResourceType::kId &&
       id.package_id() > kAppPackageId) {
     // Workaround for feature splits using package IDs > 0x7F.
     // See b/37498913.
@@ -489,7 +489,7 @@
 
   if (r_txt_printer != nullptr) {
     r_txt_printer->Print("int ")
-        .Print(to_string(name.type))
+        .Print(name.type.to_string())
         .Print(" ")
         .Print(field_name)
         .Print(" ")
@@ -497,7 +497,7 @@
   }
 
   if (out_rewrite_method != nullptr) {
-    const StringPiece& type_str = to_string(name.type);
+    const std::string type_str = name.type.to_string();
     out_rewrite_method->AppendStatement(
         StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | packageIdBits;", type_str.data(),
                      field_name.data(), type_str.data(), field_name.data()));
@@ -561,7 +561,7 @@
       return false;
     }
 
-    if (resource_name.type == ResourceType::kStyleable) {
+    if (resource_name.type.type == ResourceType::kStyleable) {
       CHECK(!entry->values.empty());
       const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
       if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 4a2d0ae..e53e220 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -358,7 +358,7 @@
     return false;
   }
 
-  switch (res->file.name.type) {
+  switch (res->file.name.type.type) {
     case ResourceType::kLayout: {
       LayoutVisitor visitor(res->file, keep_set);
       res->root->Accept(&visitor);
@@ -465,7 +465,7 @@
   locations->insert(location);
 
   // TODO: allow for more reference types if we can determine its safe.
-  if (location.name.type != ResourceType::kLayout) {
+  if (location.name.type.type != ResourceType::kLayout) {
     return false;
   }
 
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 47c804c..5372cf2 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -352,7 +352,7 @@
   }
 
   const ResourceName& ref_name = ref.name.value();
-  CHECK_EQ(ref_name.type, ResourceType::kAttr);
+  CHECK_EQ(ref_name.type.type, ResourceType::kAttr);
 
   if (!ref_name.package.empty()) {
     *out_msg << ref_name.package << ":";
@@ -385,7 +385,7 @@
   Reference transformed_reference = reference;
   xml::ResolvePackage(decls, &transformed_reference);
 
-  if (transformed_reference.name.value().type == ResourceType::kMacro) {
+  if (transformed_reference.name.value().type.type == ResourceType::kMacro) {
     if (transformed_reference.name.value().package.empty()) {
       transformed_reference.name.value().package = callsite.package;
     }
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d094d36..d78f0ac 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -349,7 +349,7 @@
   file_ref->file = file;
 
   ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
-  pkg->FindOrCreateType(file_desc.name.type)
+  pkg->FindOrCreateType(file_desc.name.type.type)
       ->FindOrCreateEntry(file_desc.name.entry)
       ->FindOrCreateValue(file_desc.config, {})
       ->value = std::move(file_ref);
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d58cbf..92b45c3 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -185,7 +185,7 @@
     const ResourceName& name) {
   std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
   if (!result) {
-    if (name.type == ResourceType::kAttr) {
+    if (name.type.type == ResourceType::kAttr) {
       // Recurse and try looking up a private attribute.
       return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
     }
@@ -203,7 +203,7 @@
         (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
   }
 
-  if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
+  if (name.type.type == ResourceType::kAttr || name.type.type == ResourceType::kAttrPrivate) {
     const ConfigDescription kDefaultConfig;
     ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
     if (config_value) {
@@ -366,7 +366,7 @@
   }
 
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (real_name.type == ResourceType::kAttr) {
+  if (real_name.type.type == ResourceType::kAttr) {
     s = LookupAttributeInTable(asset_manager_, res_id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
@@ -413,7 +413,7 @@
 
   ResourceName& name = maybe_name.value();
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (name.type == ResourceType::kAttr) {
+  if (name.type.type == ResourceType::kAttr) {
     s = LookupAttributeInTable(asset_manager_, id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 65ae7be..c17837c 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -38,7 +38,7 @@
   std::hash<std::string> str_hash;
   android::hash_t hash = 0;
   hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.package));
-  hash = android::JenkinsHashMix(hash, (uint32_t)name.type);
+  hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.type.name));
   hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.entry));
   return hash;
 }